From 606a371ec3dd28391fd4c1b8fce5a50d5650dd7c Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 6 Dec 2021 17:14:56 +0000 Subject: [PATCH 01/87] improve Data docs --- src/data.rs | 50 +++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/data.rs b/src/data.rs index b29e4ecf4..ef077e87c 100644 --- a/src/data.rs +++ b/src/data.rs @@ -31,41 +31,53 @@ pub(crate) type FnDataFactory = /// server constructs an application instance for each thread, thus application data must be /// constructed multiple times. If you want to share data between different threads, a shareable /// object should be used, e.g. `Send + Sync`. Application data does not need to be `Send` -/// or `Sync`. Internally `Data` uses `Arc`. +/// or `Sync`. Internally `Data` contains an `Arc`. /// -/// If route data is not set for a handler, using `Data` extractor would cause *Internal -/// Server Error* response. +/// If route data is not set for a handler, using `Data` extractor would cause a `500 Internal +/// Server Error` response. /// -// TODO: document `dyn T` functionality through converting an Arc -// TODO: note equivalence of req.app_data> and Data extractor -// TODO: note that data must be inserted using Data in order to extract it +/// # Unsized Data +/// For types that are unsized, most commonly `dyn T`, `Data` can wrap these types by first +/// constructing an `Arc` and using the `From` implementation to convert it. +/// +/// ``` +/// # use std::{fmt::Display, sync::Arc}; +/// # use actix_web::web::Data; +/// let displayable_arc: Arc = Arc::new(42usize); +/// let displayable_data: Data = Data::from(displayable_arc); +/// ``` /// /// # Examples /// ``` /// use std::sync::Mutex; -/// use actix_web::{web, App, HttpResponse, Responder}; +/// use actix_web::{App, HttpRequest, HttpResponse, Responder, web::{self, Data}}; /// /// struct MyData { /// counter: usize, /// } /// /// /// Use the `Data` extractor to access data in a handler. -/// async fn index(data: web::Data>) -> impl Responder { -/// let mut data = data.lock().unwrap(); -/// data.counter += 1; +/// async fn index(data: Data>) -> impl Responder { +/// let mut my_data = data.lock().unwrap(); +/// my_data.counter += 1; /// HttpResponse::Ok() /// } /// -/// fn main() { -/// let data = web::Data::new(Mutex::new(MyData{ counter: 0 })); -/// -/// let app = App::new() -/// // Store `MyData` in application storage. -/// .app_data(data.clone()) -/// .service( -/// web::resource("/index.html").route( -/// web::get().to(index))); +/// /// Alteratively, use the `HttpRequest::app_data` method to access data in a handler. +/// async fn index_alt(req: HttpRequest) -> impl Responder { +/// let data = req.app_data::>>().unwrap(); +/// let mut my_data = data.lock().unwrap(); +/// my_data.counter += 1; +/// HttpResponse::Ok() /// } +/// +/// let data = Data::new(Mutex::new(MyData { counter: 0 })); +/// +/// let app = App::new() +/// // Store `MyData` in application storage. +/// .app_data(Data::clone(&data)) +/// .route("/index.html", web::get().to(index)) +/// .route("/index-alt.html", web::get().to(index_alt)); /// ``` #[derive(Debug)] pub struct Data(Arc); From 9587261c2099ea46182147c7f8807f8b1055448e Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 7 Dec 2021 15:31:15 +0000 Subject: [PATCH 02/87] add fakeshadow's actix-web in actix-http example --- actix-http/Cargo.toml | 3 ++- actix-http/examples/actix-web.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 actix-http/examples/actix-web.rs diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 87669aeb1..6216af3d1 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -84,6 +84,7 @@ zstd = { version = "0.9", optional = true } actix-server = "2.0.0-rc.1" actix-http-test = { version = "3.0.0-beta.7", features = ["openssl"] } actix-tls = { version = "3.0.0-rc.1", features = ["openssl"] } +actix-web = "4.0.0-beta.13" async-stream = "0.3" criterion = { version = "0.3", features = ["html_reports"] } env_logger = "0.9" @@ -95,7 +96,7 @@ serde_json = "1.0" static_assertions = "1" tls-openssl = { package = "openssl", version = "0.10.9" } tls-rustls = { package = "rustls", version = "0.20.0" } -tokio = { version = "1.2", features = ["net", "rt"] } +tokio = { version = "1.2", features = ["net", "rt", "macros"] } [[example]] name = "ws" diff --git a/actix-http/examples/actix-web.rs b/actix-http/examples/actix-web.rs new file mode 100644 index 000000000..f8226507f --- /dev/null +++ b/actix-http/examples/actix-web.rs @@ -0,0 +1,26 @@ +use actix_http::HttpService; +use actix_server::Server; +use actix_service::map_config; +use actix_web::{dev::AppConfig, get, App}; + +#[get("/")] +async fn index() -> &'static str { + "Hello, world. From Actix Web!" +} + +#[tokio::main(flavor = "current_thread")] +async fn main() -> std::io::Result<()> { + Server::build() + .bind("hello-world", "127.0.0.1:8080", || { + // construct actix-web app + let app = App::new().service(index); + + HttpService::build() + // pass the app to service builder + // map_config is used to map App's configuration to ServiceBuilder + .finish(map_config(app, |_| AppConfig::default())) + .tcp() + })? + .run() + .await +} From 6460e67f8469aae30799fd2b740d7c2ba9449941 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Tue, 7 Dec 2021 23:53:04 +0800 Subject: [PATCH 03/87] remove generic body type in App. (#2493) --- src/app.rs | 53 +++++++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/src/app.rs b/src/app.rs index a27dd54a6..ab2081c18 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,9 +1,6 @@ -use std::{cell::RefCell, fmt, future::Future, marker::PhantomData, rc::Rc}; +use std::{cell::RefCell, fmt, future::Future, rc::Rc}; -use actix_http::{ - body::{BoxBody, MessageBody}, - Extensions, Request, -}; +use actix_http::{body::MessageBody, Extensions, Request}; use actix_service::{ apply, apply_fn_factory, boxed, IntoServiceFactory, ServiceFactory, ServiceFactoryExt, Transform, @@ -26,7 +23,7 @@ use crate::{ /// Application builder - structure that follows the builder pattern /// for building application instances. -pub struct App { +pub struct App { endpoint: T, services: Vec>, default: Option>, @@ -34,10 +31,9 @@ pub struct App { data_factories: Vec, external: Vec, extensions: Extensions, - _phantom: PhantomData, } -impl App { +impl App { /// Create application builder. Application can be configured with a builder-like pattern. #[allow(clippy::new_without_default)] pub fn new() -> Self { @@ -51,22 +47,11 @@ impl App { factory_ref, external: Vec::new(), extensions: Extensions::new(), - _phantom: PhantomData, } } } -impl App -where - B: MessageBody, - T: ServiceFactory< - ServiceRequest, - Config = (), - Response = ServiceResponse, - Error = Error, - InitError = (), - >, -{ +impl App { /// Set application (root level) data. /// /// Application data stored with `App::app_data()` method is available through the @@ -365,7 +350,7 @@ where /// .route("/index.html", web::get().to(index)); /// } /// ``` - pub fn wrap( + pub fn wrap( self, mw: M, ) -> App< @@ -376,9 +361,16 @@ where Error = Error, InitError = (), >, - B1, > where + T: ServiceFactory< + ServiceRequest, + Response = ServiceResponse, + Error = Error, + Config = (), + InitError = (), + >, + B: MessageBody, M: Transform< T::Service, ServiceRequest, @@ -396,7 +388,6 @@ where factory_ref: self.factory_ref, external: self.external, extensions: self.extensions, - _phantom: PhantomData, } } @@ -431,7 +422,7 @@ where /// .route("/index.html", web::get().to(index)); /// } /// ``` - pub fn wrap_fn( + pub fn wrap_fn( self, mw: F, ) -> App< @@ -442,12 +433,19 @@ where Error = Error, InitError = (), >, - B1, > where - B1: MessageBody, + T: ServiceFactory< + ServiceRequest, + Response = ServiceResponse, + Error = Error, + Config = (), + InitError = (), + >, + B: MessageBody, F: Fn(ServiceRequest, &T::Service) -> R + Clone, R: Future, Error>>, + B1: MessageBody, { App { endpoint: apply_fn_factory(self.endpoint, mw), @@ -457,12 +455,11 @@ where factory_ref: self.factory_ref, external: self.external, extensions: self.extensions, - _phantom: PhantomData, } } } -impl IntoServiceFactory, Request> for App +impl IntoServiceFactory, Request> for App where B: MessageBody, T: ServiceFactory< From 069cf2da0792d7a2160e4d6a5345104c80aa7967 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 8 Dec 2021 00:26:28 +0800 Subject: [PATCH 04/87] enable scope middleware with generic res body. (#2492) Co-authored-by: Rob Ede --- CHANGES.md | 4 ++++ src/middleware/compat.rs | 2 +- src/scope.rs | 49 ++++++++++++++++++++-------------------- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 1b108fee6..2adf54d3d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -13,6 +13,8 @@ * Rename `Accept::{mime_preference => preference}`. [#2480] * Un-deprecate `App::data_factory`. [#2484] * `HttpRequest::url_for` no longer constructs URLs with query or fragment components. [#2430] +* Remove `B` (body) type parameter on `App`. [#2493] +* Add `B` (body) type parameter on `Scope`. [#2492] ### Fixed * Accept wildcard `*` items in `AcceptLanguage`. [#2480] @@ -25,6 +27,8 @@ [#2482]: https://github.com/actix/actix-web/pull/2482 [#2484]: https://github.com/actix/actix-web/pull/2484 [#2485]: https://github.com/actix/actix-web/pull/2485 +[#2492]: https://github.com/actix/actix-web/pull/2492 +[#2493]: https://github.com/actix/actix-web/pull/2493 ## 4.0.0-beta.13 - 2021-11-30 diff --git a/src/middleware/compat.rs b/src/middleware/compat.rs index e6ef1806f..ed441f7b9 100644 --- a/src/middleware/compat.rs +++ b/src/middleware/compat.rs @@ -154,7 +154,7 @@ mod tests { let srv = init_service( App::new().service( web::scope("app") - .wrap(Compat::new(logger)) + .wrap(logger) .wrap(Compat::new(compress)) .service(web::resource("/test").route(web::get().to(HttpResponse::Ok))), ), diff --git a/src/scope.rs b/src/scope.rs index ad102b66b..74523cd94 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,6 +1,6 @@ -use std::{cell::RefCell, fmt, future::Future, mem, rc::Rc}; +use std::{cell::RefCell, fmt, future::Future, marker::PhantomData, mem, rc::Rc}; -use actix_http::Extensions; +use actix_http::{body::BoxBody, Extensions}; use actix_router::{ResourceDef, Router}; use actix_service::{ apply, apply_fn_factory, boxed, IntoServiceFactory, Service, ServiceFactory, @@ -52,7 +52,7 @@ type Guards = Vec>; /// * /{project_id}/path1 - responds to all http method /// * /{project_id}/path2 - `GET` requests /// * /{project_id}/path3 - `HEAD` requests -pub struct Scope { +pub struct Scope { endpoint: T, rdef: String, app_data: Option, @@ -61,6 +61,7 @@ pub struct Scope { default: Option>, external: Vec, factory_ref: Rc>>, + _phantom: PhantomData, } impl Scope { @@ -77,19 +78,21 @@ impl Scope { default: None, external: Vec::new(), factory_ref, + _phantom: Default::default(), } } } -impl Scope +impl Scope where T: ServiceFactory< ServiceRequest, Config = (), - Response = ServiceResponse, + Response = ServiceResponse, Error = Error, InitError = (), >, + B: 'static, { /// Add match guard to a scope. /// @@ -295,32 +298,29 @@ where self } - /// Registers middleware, in the form of a middleware component (type), - /// that runs during inbound processing in the request - /// life-cycle (request -> response), modifying request as - /// necessary, across all requests managed by the *Scope*. Scope-level - /// middleware is more limited in what it can modify, relative to Route or - /// Application level middleware, in that Scope-level middleware can not modify - /// ServiceResponse. + /// Registers middleware, in the form of a middleware component (type), that runs during inbound + /// processing in the request life-cycle (request -> response), modifying request as necessary, + /// across all requests managed by the *Scope*. /// /// Use middleware when you need to read or modify *every* request in some way. - pub fn wrap( + pub fn wrap( self, mw: M, ) -> Scope< impl ServiceFactory< ServiceRequest, Config = (), - Response = ServiceResponse, + Response = ServiceResponse, Error = Error, InitError = (), >, + B1, > where M: Transform< T::Service, ServiceRequest, - Response = ServiceResponse, + Response = ServiceResponse, Error = Error, InitError = (), >, @@ -334,16 +334,15 @@ where default: self.default, external: self.external, factory_ref: self.factory_ref, + _phantom: PhantomData, } } - /// Registers middleware, in the form of a closure, that runs during inbound - /// processing in the request life-cycle (request -> response), modifying - /// request as necessary, across all requests managed by the *Scope*. - /// Scope-level middleware is more limited in what it can modify, relative - /// to Route or Application level middleware, in that Scope-level middleware - /// can not modify ServiceResponse. + /// Registers middleware, in the form of a closure, that runs during inbound processing in the + /// request life-cycle (request -> response), modifying request as necessary, across all + /// requests managed by the *Scope*. /// + /// # Examples /// ``` /// use actix_service::Service; /// use actix_web::{web, App}; @@ -369,21 +368,22 @@ where /// .route("/index.html", web::get().to(index))); /// } /// ``` - pub fn wrap_fn( + pub fn wrap_fn( self, mw: F, ) -> Scope< impl ServiceFactory< ServiceRequest, Config = (), - Response = ServiceResponse, + Response = ServiceResponse, Error = Error, InitError = (), >, + B1, > where F: Fn(ServiceRequest, &T::Service) -> R + Clone, - R: Future>, + R: Future, Error>>, { Scope { endpoint: apply_fn_factory(self.endpoint, mw), @@ -394,6 +394,7 @@ where default: self.default, external: self.external, factory_ref: self.factory_ref, + _phantom: PhantomData, } } } From d35b7644dccacdc851cd7c52a7bde92a2cd4e86a Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 7 Dec 2021 17:23:34 +0000 Subject: [PATCH 05/87] add connection level data container (#2491) --- CHANGES.md | 2 ++ actix-http/CHANGES.md | 3 ++ actix-http/src/extensions.rs | 37 ++++----------------- actix-http/src/h1/dispatcher.rs | 56 +++++++++++++++++--------------- actix-http/src/h1/service.rs | 12 ++----- actix-http/src/h2/dispatcher.rs | 13 ++++---- actix-http/src/h2/service.rs | 13 +++++--- actix-http/src/lib.rs | 10 +----- actix-http/src/request.rs | 30 ++++++++++++++++- actix-http/src/service.rs | 18 ++++------ actix-http/tests/test_openssl.rs | 4 +-- actix-http/tests/test_server.rs | 4 +-- examples/on_connect.rs | 25 ++++++++++---- src/app_service.rs | 5 ++- src/request.rs | 17 ++++++++++ src/server.rs | 4 +-- src/service.rs | 18 ++++++---- src/test.rs | 6 ++-- 18 files changed, 152 insertions(+), 125 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2adf54d3d..2ef1478dc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ * `Range` typed header. [#2485] * `HttpResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] * `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] +* Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491] ### Changed * Rename `Accept::{mime_precedence => ranked}`. [#2480] @@ -27,6 +28,7 @@ [#2482]: https://github.com/actix/actix-web/pull/2482 [#2484]: https://github.com/actix/actix-web/pull/2484 [#2485]: https://github.com/actix/actix-web/pull/2485 +[#2491]: https://github.com/actix/actix-web/pull/2491 [#2492]: https://github.com/actix/actix-web/pull/2492 [#2493]: https://github.com/actix/actix-web/pull/2493 diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 1a59b233a..f435784d8 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -14,6 +14,8 @@ * `header::QualityItem::{max, min}`. [#2486] * `header::Quality::{MAX, MIN}`. [#2486] * `impl Display` for `header::Quality`. [#2486] +* Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491] +* `Request::take_conn_data()`. [#2491] ### Changed * Rename `body::BoxBody::{from_body => new}`. [#2468] @@ -39,6 +41,7 @@ [#1920]: https://github.com/actix/actix-web/pull/1920 [#2486]: https://github.com/actix/actix-web/pull/2486 [#2488]: https://github.com/actix/actix-web/pull/2488 +[#2491]: https://github.com/actix/actix-web/pull/2491 ## 3.0.0-beta.14 - 2021-11-30 diff --git a/actix-http/src/extensions.rs b/actix-http/src/extensions.rs index 5fdcefd6d..164919d87 100644 --- a/actix-http/src/extensions.rs +++ b/actix-http/src/extensions.rs @@ -1,6 +1,6 @@ use std::{ any::{Any, TypeId}, - fmt, mem, + fmt, }; use ahash::AHashMap; @@ -10,8 +10,7 @@ use ahash::AHashMap; /// All entries into this map must be owned types (or static references). #[derive(Default)] pub struct Extensions { - /// Use FxHasher with a std HashMap with for faster - /// lookups on the small `TypeId` (u64 equivalent) keys. + /// Use AHasher with a std HashMap with for faster lookups on the small `TypeId` keys. map: AHashMap>, } @@ -123,11 +122,6 @@ impl Extensions { pub fn extend(&mut self, other: Extensions) { self.map.extend(other.map); } - - /// Sets (or overrides) items from `other` into this map. - pub(crate) fn drain_from(&mut self, other: &mut Self) { - self.map.extend(mem::take(&mut other.map)); - } } impl fmt::Debug for Extensions { @@ -179,6 +173,8 @@ mod tests { #[test] fn test_integers() { + static A: u32 = 8; + let mut map = Extensions::new(); map.insert::(8); @@ -191,6 +187,7 @@ mod tests { map.insert::(32); map.insert::(64); map.insert::(128); + map.insert::<&'static u32>(&A); assert!(map.get::().is_some()); assert!(map.get::().is_some()); assert!(map.get::().is_some()); @@ -201,6 +198,7 @@ mod tests { assert!(map.get::().is_some()); assert!(map.get::().is_some()); assert!(map.get::().is_some()); + assert!(map.get::<&'static u32>().is_some()); } #[test] @@ -279,27 +277,4 @@ mod tests { assert_eq!(extensions.get(), Some(&20u8)); assert_eq!(extensions.get_mut(), Some(&mut 20u8)); } - - #[test] - fn test_drain_from() { - let mut ext = Extensions::new(); - ext.insert(2isize); - - let mut more_ext = Extensions::new(); - - more_ext.insert(5isize); - more_ext.insert(5usize); - - assert_eq!(ext.get::(), Some(&2isize)); - assert_eq!(ext.get::(), None); - assert_eq!(more_ext.get::(), Some(&5isize)); - assert_eq!(more_ext.get::(), Some(&5usize)); - - ext.drain_from(&mut more_ext); - - assert_eq!(ext.get::(), Some(&5isize)); - assert_eq!(ext.get::(), Some(&5usize)); - assert_eq!(more_ext.get::(), None); - assert_eq!(more_ext.get::(), None); - } } diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 3c36e7367..b11054307 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -22,7 +22,7 @@ use crate::{ config::ServiceConfig, error::{DispatchError, ParseError, PayloadError}, service::HttpFlow, - OnConnectData, Request, Response, StatusCode, + Extensions, OnConnectData, Request, Response, StatusCode, }; use super::{ @@ -100,9 +100,9 @@ where U::Error: fmt::Display, { flow: Rc>, - on_connect_data: OnConnectData, flags: Flags, peer_addr: Option, + conn_data: Option>, error: Option, #[pin] @@ -179,10 +179,10 @@ where /// Create HTTP/1 dispatcher. pub(crate) fn new( io: T, - config: ServiceConfig, flow: Rc>, - on_connect_data: OnConnectData, + config: ServiceConfig, peer_addr: Option, + conn_data: OnConnectData, ) -> Self { let flags = if config.keep_alive_enabled() { Flags::KEEPALIVE @@ -198,20 +198,23 @@ where Dispatcher { inner: DispatcherState::Normal(InnerDispatcher { - read_buf: BytesMut::with_capacity(HW_BUFFER_SIZE), - write_buf: BytesMut::with_capacity(HW_BUFFER_SIZE), - payload: None, - state: State::None, - error: None, - messages: VecDeque::new(), - io: Some(io), - codec: Codec::new(config), flow, - on_connect_data, flags, peer_addr, + conn_data: conn_data.0.map(Rc::new), + error: None, + + state: State::None, + payload: None, + messages: VecDeque::new(), + ka_expire, ka_timer, + + io: Some(io), + read_buf: BytesMut::with_capacity(HW_BUFFER_SIZE), + write_buf: BytesMut::with_capacity(HW_BUFFER_SIZE), + codec: Codec::new(config), }), #[cfg(test)] @@ -593,8 +596,7 @@ where Message::Item(mut req) => { req.head_mut().peer_addr = *this.peer_addr; - // merge on_connect_ext data into request extensions - this.on_connect_data.merge_into(&mut req); + req.conn_data = this.conn_data.as_ref().map(Rc::clone); match this.codec.message_type() { // Request is upgradable. add upgrade message and break. @@ -1100,10 +1102,10 @@ mod tests { let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new( buf, - ServiceConfig::default(), services, - OnConnectData::default(), + ServiceConfig::default(), None, + OnConnectData::default(), ); actix_rt::pin!(h1); @@ -1140,10 +1142,10 @@ mod tests { let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new( buf, - cfg, services, - OnConnectData::default(), + cfg, None, + OnConnectData::default(), ); actix_rt::pin!(h1); @@ -1194,10 +1196,10 @@ mod tests { let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new( buf, - cfg, services, - OnConnectData::default(), + cfg, None, + OnConnectData::default(), ); actix_rt::pin!(h1); @@ -1244,10 +1246,10 @@ mod tests { let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new( buf.clone(), - cfg, services, - OnConnectData::default(), + cfg, None, + OnConnectData::default(), ); buf.extend_read_buf( @@ -1316,10 +1318,10 @@ mod tests { let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new( buf.clone(), - cfg, services, - OnConnectData::default(), + cfg, None, + OnConnectData::default(), ); buf.extend_read_buf( @@ -1393,10 +1395,10 @@ mod tests { let h1 = Dispatcher::<_, _, _, _, TestUpgrade>::new( buf.clone(), - cfg, services, - OnConnectData::default(), + cfg, None, + OnConnectData::default(), ); buf.extend_read_buf( diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index 70e83901c..fd9635690 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -365,15 +365,7 @@ where } fn call(&self, (io, addr): (T, Option)) -> Self::Future { - let on_connect_data = - OnConnectData::from_io(&io, self.on_connect_ext.as_deref()); - - Dispatcher::new( - io, - self.cfg.clone(), - self.flow.clone(), - on_connect_data, - addr, - ) + let conn_data = OnConnectData::from_io(&io, self.on_connect_ext.as_deref()); + Dispatcher::new(io, self.flow.clone(), self.cfg.clone(), addr, conn_data) } } diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 6d2f4579a..22eab6c28 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -27,7 +27,7 @@ use crate::{ body::{BodySize, BoxBody, MessageBody}, config::ServiceConfig, service::HttpFlow, - OnConnectData, Payload, Request, Response, ResponseHead, + Extensions, OnConnectData, Payload, Request, Response, ResponseHead, }; const CHUNK_SIZE: usize = 16_384; @@ -37,7 +37,7 @@ pin_project! { pub struct Dispatcher { flow: Rc>, connection: Connection, - on_connect_data: OnConnectData, + conn_data: Option>, config: ServiceConfig, peer_addr: Option, ping_pong: Option, @@ -50,11 +50,11 @@ where T: AsyncRead + AsyncWrite + Unpin, { pub(crate) fn new( - flow: Rc>, mut conn: Connection, - on_connect_data: OnConnectData, + flow: Rc>, config: ServiceConfig, peer_addr: Option, + conn_data: OnConnectData, timer: Option>>, ) -> Self { let ping_pong = config.keep_alive().map(|dur| H2PingPong { @@ -74,7 +74,7 @@ where config, peer_addr, connection: conn, - on_connect_data, + conn_data: conn_data.0.map(Rc::new), ping_pong, _phantom: PhantomData, } @@ -119,8 +119,7 @@ where head.headers = parts.headers.into(); head.peer_addr = this.peer_addr; - // merge on_connect_ext data into request extensions - this.on_connect_data.merge_into(&mut req); + req.conn_data = this.conn_data.as_ref().map(Rc::clone); let fut = this.flow.service.call(req); let config = this.config.clone(); diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index 8a9061b94..aa2a6cc69 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -1,7 +1,7 @@ use std::{ future::Future, marker::PhantomData, - net, + mem, net, pin::Pin, rc::Rc, task::{Context, Poll}, @@ -339,21 +339,24 @@ where ref mut srv, ref mut config, ref peer_addr, - ref mut on_connect_data, + ref mut conn_data, ref mut handshake, ) => match ready!(Pin::new(handshake).poll(cx)) { Ok((conn, timer)) => { - let on_connect_data = std::mem::take(on_connect_data); + let on_connect_data = mem::take(conn_data); + self.state = State::Incoming(Dispatcher::new( - srv.take().unwrap(), conn, - on_connect_data, + srv.take().unwrap(), config.take().unwrap(), *peer_addr, + on_connect_data, timer, )); + self.poll(cx) } + Err(err) => { trace!("H2 handshake error: {}", err); Poll::Ready(Err(err)) diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index aeba3da36..89ee139c0 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -92,19 +92,11 @@ impl OnConnectData { on_connect_ext: Option<&ConnectCallback>, ) -> Self { let ext = on_connect_ext.map(|handler| { - let mut extensions = Extensions::new(); + let mut extensions = Extensions::default(); handler(io, &mut extensions); extensions }); Self(ext) } - - /// Merge self into given request's extensions. - #[inline] - pub(crate) fn merge_into(&mut self, req: &mut Request) { - if let Some(ref mut ext) = self.0 { - req.head.extensions.get_mut().drain_from(ext); - } - } } diff --git a/actix-http/src/request.rs b/actix-http/src/request.rs index 401e9745c..78c0527b5 100644 --- a/actix-http/src/request.rs +++ b/actix-http/src/request.rs @@ -2,7 +2,9 @@ use std::{ cell::{Ref, RefMut}, - fmt, net, str, + fmt, net, + rc::Rc, + str, }; use http::{header, Method, Uri, Version}; @@ -19,6 +21,7 @@ use crate::{ pub struct Request

{ pub(crate) payload: Payload

, pub(crate) head: Message, + pub(crate) conn_data: Option>, } impl

HttpMessage for Request

{ @@ -51,6 +54,7 @@ impl From> for Request { Request { head, payload: Payload::None, + conn_data: None, } } } @@ -61,6 +65,7 @@ impl Request { Request { head: Message::new(), payload: Payload::None, + conn_data: None, } } } @@ -71,16 +76,19 @@ impl

Request

{ Request { payload, head: Message::new(), + conn_data: None, } } /// Create new Request instance pub fn replace_payload(self, payload: Payload) -> (Request, Payload

) { let pl = self.payload; + ( Request { payload, head: self.head, + conn_data: self.conn_data, }, pl, ) @@ -170,6 +178,26 @@ impl

Request

{ pub fn peer_addr(&self) -> Option { self.head().peer_addr } + + /// Returns a reference a piece of connection data set in an [on-connect] callback. + /// + /// ```ignore + /// let opt_t = req.conn_data::(); + /// ``` + /// + /// [on-connect]: crate::HttpServiceBuilder::on_connect_ext + pub fn conn_data(&self) -> Option<&T> { + self.conn_data + .as_deref() + .and_then(|container| container.get::()) + } + + /// Returns the connection data container if an [on-connect] callback was registered. + /// + /// [on-connect]: crate::HttpServiceBuilder::on_connect_ext + pub fn take_conn_data(&mut self) -> Option> { + self.conn_data.take() + } } impl

fmt::Debug for Request

{ diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 7af34ba05..cba4c1756 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -507,8 +507,7 @@ where &self, (io, proto, peer_addr): (T, Protocol, Option), ) -> Self::Future { - let on_connect_data = - OnConnectData::from_io(&io, self.on_connect_ext.as_deref()); + let conn_data = OnConnectData::from_io(&io, self.on_connect_ext.as_deref()); match proto { Protocol::Http2 => HttpServiceHandlerResponse { @@ -517,7 +516,7 @@ where h2::handshake_with_timeout(io, &self.cfg), self.cfg.clone(), self.flow.clone(), - on_connect_data, + conn_data, peer_addr, )), }, @@ -527,10 +526,10 @@ where state: State::H1 { dispatcher: h1::Dispatcher::new( io, - self.cfg.clone(), self.flow.clone(), - on_connect_data, + self.cfg.clone(), peer_addr, + conn_data, ), }, }, @@ -627,17 +626,12 @@ where StateProj::H2Handshake { handshake: data } => { match ready!(Pin::new(&mut data.as_mut().unwrap().0).poll(cx)) { Ok((conn, timer)) => { - let (_, config, flow, on_connect_data, peer_addr) = + let (_, config, flow, conn_data, peer_addr) = data.take().unwrap(); self.as_mut().project().state.set(State::H2 { dispatcher: h2::Dispatcher::new( - flow, - conn, - on_connect_data, - config, - peer_addr, - timer, + conn, flow, config, peer_addr, conn_data, timer, ), }); self.poll(cx) diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index 6f68cc04d..8ba41b4bd 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -8,7 +8,7 @@ use actix_http::{ body::{BodyStream, BoxBody, SizedStream}, error::PayloadError, header::{self, HeaderValue}, - Error, HttpMessage, HttpService, Method, Request, Response, StatusCode, Version, + Error, HttpService, Method, Request, Response, StatusCode, Version, }; use actix_http_test::test_server; use actix_service::{fn_service, ServiceFactoryExt}; @@ -430,7 +430,7 @@ async fn test_h2_on_connect() { data.insert(20isize); }) .h2(|req: Request| { - assert!(req.extensions().contains::()); + assert!(req.conn_data::().is_some()); ok::<_, Infallible>(Response::ok()) }) .openssl(tls_config()) diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index e6733b29b..b7fde877f 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -7,7 +7,7 @@ use std::{ use actix_http::{ body::{self, BodyStream, BoxBody, SizedStream}, - header, Error, HttpMessage, HttpService, KeepAlive, Request, Response, StatusCode, + header, Error, HttpService, KeepAlive, Request, Response, StatusCode, }; use actix_http_test::test_server; use actix_rt::time::sleep; @@ -748,7 +748,7 @@ async fn test_h1_on_connect() { data.insert(20isize); }) .h1(|req: Request| { - assert!(req.extensions().contains::()); + assert!(req.conn_data::().is_some()); ok::<_, Infallible>(Response::ok()) }) .tcp() diff --git a/examples/on_connect.rs b/examples/on_connect.rs index 9709835e6..d76e9ce56 100644 --- a/examples/on_connect.rs +++ b/examples/on_connect.rs @@ -6,7 +6,10 @@ use std::{any::Any, io, net::SocketAddr}; -use actix_web::{dev::Extensions, rt::net::TcpStream, web, App, HttpServer}; +use actix_web::{ + dev::Extensions, rt::net::TcpStream, web, App, HttpRequest, HttpResponse, HttpServer, + Responder, +}; #[allow(dead_code)] #[derive(Debug, Clone)] @@ -16,11 +19,16 @@ struct ConnectionInfo { ttl: Option, } -async fn route_whoami(conn_info: web::ReqData) -> String { - format!( - "Here is some info about your connection:\n\n{:#?}", - conn_info - ) +async fn route_whoami(req: HttpRequest) -> impl Responder { + match req.conn_data::() { + Some(info) => HttpResponse::Ok().body(format!( + "Here is some info about your connection:\n\n{:#?}", + info + )), + None => { + HttpResponse::InternalServerError().body("Missing expected request extension data") + } + } } fn get_conn_info(connection: &dyn Any, data: &mut Extensions) { @@ -39,9 +47,12 @@ fn get_conn_info(connection: &dyn Any, data: &mut Extensions) { async fn main() -> io::Result<()> { env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); + let bind = ("127.0.0.1", 8080); + log::info!("staring server at http://{}:{}", &bind.0, &bind.1); + HttpServer::new(|| App::new().default_service(web::to(route_whoami))) .on_connect(get_conn_info) - .bind(("127.0.0.1", 8080))? + .bind(bind)? .workers(1) .run() .await diff --git a/src/app_service.rs b/src/app_service.rs index bca8f2629..5dfc3b5ae 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -197,7 +197,8 @@ where actix_service::forward_ready!(service); - fn call(&self, req: Request) -> Self::Future { + fn call(&self, mut req: Request) -> Self::Future { + let conn_data = req.take_conn_data(); let (head, payload) = req.into_parts(); let req = if let Some(mut req) = self.app_state.pool().pop() { @@ -205,6 +206,7 @@ where inner.path.get_mut().update(&head.uri); inner.path.reset(); inner.head = head; + inner.conn_data = conn_data; req } else { HttpRequest::new( @@ -212,6 +214,7 @@ where head, self.app_state.clone(), self.app_data.clone(), + conn_data, ) }; self.service.call(ServiceRequest::new(req, payload)) diff --git a/src/request.rs b/src/request.rs index f04d47c6f..b7f4f3510 100644 --- a/src/request.rs +++ b/src/request.rs @@ -37,6 +37,7 @@ pub(crate) struct HttpRequestInner { pub(crate) head: Message, pub(crate) path: Path, pub(crate) app_data: SmallVec<[Rc; 4]>, + pub(crate) conn_data: Option>, app_state: Rc, } @@ -47,6 +48,7 @@ impl HttpRequest { head: Message, app_state: Rc, app_data: Rc, + conn_data: Option>, ) -> HttpRequest { let mut data = SmallVec::<[Rc; 4]>::new(); data.push(app_data); @@ -57,6 +59,7 @@ impl HttpRequest { path, app_state, app_data: data, + conn_data, }), } } @@ -165,6 +168,20 @@ impl HttpRequest { self.head().extensions_mut() } + /// Returns a reference a piece of connection data set in an [on-connect] callback. + /// + /// ```ignore + /// let opt_t = req.conn_data::(); + /// ``` + /// + /// [on-connect]: crate::HttpServiceBuilder::on_connect_ext + pub fn conn_data(&self) -> Option<&T> { + self.inner + .conn_data + .as_deref() + .and_then(|container| container.get::()) + } + /// Generates URL for a named resource. /// /// This substitutes in sequence all URL parameters that appear in the resource itself and in diff --git a/src/server.rs b/src/server.rs index 3db849410..b2ff423f1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -101,9 +101,9 @@ where /// Sets function that will be called once before each connection is handled. /// It will receive a `&std::any::Any`, which contains underlying connection type and an - /// [Extensions] container so that request-local data can be passed to middleware and handlers. + /// [Extensions] container so that connection data can be accessed in middleware and handlers. /// - /// For example: + /// # Connection Types /// - `actix_tls::accept::openssl::TlsStream` when using openssl. /// - `actix_tls::accept::rustls::TlsStream` when using rustls. /// - `actix_web::rt::net::TcpStream` when no encryption is used. diff --git a/src/service.rs b/src/service.rs index 4185d6018..d56752f13 100644 --- a/src/service.rs +++ b/src/service.rs @@ -172,12 +172,10 @@ impl ServiceRequest { self.head().uri.path() } - /// The query string in the URL. - /// - /// E.g., id=10 + /// Counterpart to [`HttpRequest::query_string`](super::HttpRequest::query_string()). #[inline] pub fn query_string(&self) -> &str { - self.uri().query().unwrap_or_default() + self.req.query_string() } /// Peer socket address. @@ -241,6 +239,7 @@ impl ServiceRequest { } /// Counterpart to [`HttpRequest::app_data`](super::HttpRequest::app_data()). + #[inline] pub fn app_data(&self) -> Option<&T> { for container in self.req.inner.app_data.iter().rev() { if let Some(data) = container.get::() { @@ -251,6 +250,12 @@ impl ServiceRequest { None } + /// Counterpart to [`HttpRequest::conn_data`](super::HttpRequest::conn_data()). + #[inline] + pub fn conn_data(&self) -> Option<&T> { + self.req.conn_data() + } + #[cfg(feature = "cookies")] pub fn cookies(&self) -> Result>>, CookieParseError> { self.req.cookies() @@ -263,6 +268,7 @@ impl ServiceRequest { } /// Set request payload. + #[inline] pub fn set_payload(&mut self, payload: Payload) { self.payload = payload; } @@ -280,6 +286,7 @@ impl ServiceRequest { } impl Resource for ServiceRequest { + #[inline] fn resource_path(&mut self) -> &mut Path { self.match_info_mut() } @@ -404,12 +411,11 @@ impl ServiceResponse { } /// Extract response body + #[inline] pub fn into_body(self) -> B { self.response.into_body() } -} -impl ServiceResponse { /// Set a new body #[inline] pub fn map_body(self, f: F) -> ServiceResponse diff --git a/src/test.rs b/src/test.rs index 07d2d16b6..bff9c62dc 100644 --- a/src/test.rs +++ b/src/test.rs @@ -581,7 +581,7 @@ impl TestRequest { let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone()); ServiceRequest::new( - HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data)), + HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data), None), payload, ) } @@ -599,7 +599,7 @@ impl TestRequest { let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone()); - HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data)) + HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data), None) } /// Complete request creation and generate `HttpRequest` and `Payload` instances @@ -610,7 +610,7 @@ impl TestRequest { let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone()); - let req = HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data)); + let req = HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data), None); (req, payload) } From e49e559f47dd3642120dcccee5efaa94cb690e4d Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 8 Dec 2021 05:43:50 +0000 Subject: [PATCH 06/87] fix some docs --- actix-http/src/h1/dispatcher.rs | 71 ++++++++++++++---------------- actix-http/src/h2/dispatcher.rs | 2 +- actix-http/src/header/as_name.rs | 2 +- actix-http/src/header/into_pair.rs | 2 +- actix-http/src/response_builder.rs | 6 ++- src/error/mod.rs | 2 +- src/request.rs | 2 +- 7 files changed, 41 insertions(+), 46 deletions(-) diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index b11054307..d2410be1e 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -27,6 +27,7 @@ use crate::{ use super::{ codec::Codec, + decoder::MAX_BUFFER_SIZE, payload::{Payload, PayloadSender, PayloadStatus}, Message, MessageType, }; @@ -793,7 +794,6 @@ where /// Returns true when io stream can be disconnected after write to it. /// /// It covers these conditions: - /// /// - `std::io::ErrorKind::ConnectionReset` after partial read. /// - all data read done. #[inline(always)] @@ -813,46 +813,39 @@ where loop { // Return early when read buf exceed decoder's max buffer size. - if this.read_buf.len() >= super::decoder::MAX_BUFFER_SIZE { - /* - At this point it's not known IO stream is still scheduled - to be waked up. so force wake up dispatcher just in case. + if this.read_buf.len() >= MAX_BUFFER_SIZE { + // At this point it's not known IO stream is still scheduled to be waked up so + // force wake up dispatcher just in case. + // + // Reason: + // AsyncRead mostly would only have guarantee wake up when the poll_read + // return Poll::Pending. + // + // Case: + // When read_buf is beyond max buffer size the early return could be successfully + // be parsed as a new Request. This case would not generate ParseError::TooLarge and + // at this point IO stream is not fully read to Pending and would result in + // dispatcher stuck until timeout (KA) + // + // Note: + // This is a perf choice to reduce branch on ::decode. + // + // A Request head too large to parse is only checked on + // `httparse::Status::Partial` condition. - Reason: - AsyncRead mostly would only have guarantee wake up - when the poll_read return Poll::Pending. - - Case: - When read_buf is beyond max buffer size the early return - could be successfully be parsed as a new Request. - This case would not generate ParseError::TooLarge - and at this point IO stream is not fully read to Pending - and would result in dispatcher stuck until timeout (KA) - - Note: - This is a perf choice to reduce branch on - ::decode. - - A Request head too large to parse is only checked on - httparse::Status::Partial condition. - */ if this.payload.is_none() { - /* - When dispatcher has a payload the responsibility of - wake up it would be shift to h1::payload::Payload. - - Reason: - Self wake up when there is payload would waste poll - and/or result in over read. - - Case: - When payload is (partial) dropped by user there is - no need to do read anymore. - At this case read_buf could always remain beyond - MAX_BUFFER_SIZE and self wake up would be busy poll - dispatcher and waste resource. - - */ + // When dispatcher has a payload the responsibility of wake up it would be shift + // to h1::payload::Payload. + // + // Reason: + // Self wake up when there is payload would waste poll and/or result in + // over read. + // + // Case: + // When payload is (partial) dropped by user there is no need to do + // read anymore. At this case read_buf could always remain beyond + // MAX_BUFFER_SIZE and self wake up would be busy poll dispatcher and + // waste resources. cx.waker().wake_by_ref(); } diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 22eab6c28..55f71122b 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -109,7 +109,7 @@ where Poll::Ready(Some((req, tx))) => { let (parts, body) = req.into_parts(); let pl = crate::h2::Payload::new(body); - let pl = Payload::::H2(pl); + let pl = Payload::H2(pl); let mut req = Request::with_payload(pl); let head = req.head_mut(); diff --git a/actix-http/src/header/as_name.rs b/actix-http/src/header/as_name.rs index 04d32c41d..17d007f2f 100644 --- a/actix-http/src/header/as_name.rs +++ b/actix-http/src/header/as_name.rs @@ -6,7 +6,7 @@ use http::header::{HeaderName, InvalidHeaderName}; /// Sealed trait implemented for types that can be effectively borrowed as a [`HeaderValue`]. /// -/// [`HeaderValue`]: crate::http::HeaderValue +/// [`HeaderValue`]: super::HeaderValue pub trait AsHeaderName: Sealed {} pub struct Seal; diff --git a/actix-http/src/header/into_pair.rs b/actix-http/src/header/into_pair.rs index 472700548..b4250e06e 100644 --- a/actix-http/src/header/into_pair.rs +++ b/actix-http/src/header/into_pair.rs @@ -12,7 +12,7 @@ use super::{Header, IntoHeaderValue}; /// An interface for types that can be converted into a [`HeaderName`]/[`HeaderValue`] pair for /// insertion into a [`HeaderMap`]. /// -/// [`HeaderMap`]: crate::http::HeaderMap +/// [`HeaderMap`]: super::HeaderMap pub trait IntoHeaderPair: Sized { type Error: Into; diff --git a/actix-http/src/response_builder.rs b/actix-http/src/response_builder.rs index f11f89219..dfc2612fb 100644 --- a/actix-http/src/response_builder.rs +++ b/actix-http/src/response_builder.rs @@ -47,7 +47,8 @@ impl ResponseBuilder { /// Create response builder /// /// # Examples - // /// use actix_http::{Response, ResponseBuilder, StatusCode};, / `` + /// ``` + /// use actix_http::{Response, ResponseBuilder, StatusCode}; /// let res: Response<_> = ResponseBuilder::default().finish(); /// assert_eq!(res.status(), StatusCode::OK); /// ``` @@ -62,7 +63,8 @@ impl ResponseBuilder { /// Set HTTP status code of this response. /// /// # Examples - // /// use actix_http::{ResponseBuilder, StatusCode};, / `` + /// ``` + /// use actix_http::{ResponseBuilder, StatusCode}; /// let res = ResponseBuilder::default().status(StatusCode::NOT_FOUND).finish(); /// assert_eq!(res.status(), StatusCode::NOT_FOUND); /// ``` diff --git a/src/error/mod.rs b/src/error/mod.rs index 90c2c9a61..48f71618c 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -2,7 +2,7 @@ /// This is meant to be a glob import of the whole error module, but rustdoc can't handle /// shadowing `Error` type, so it is expanded manually. -/// See https://github.com/rust-lang/rust/issues/83375 +/// See pub use actix_http::error::{ BlockingError, ContentTypeError, DispatchError, HttpError, ParseError, PayloadError, }; diff --git a/src/request.rs b/src/request.rs index b7f4f3510..d99849eef 100644 --- a/src/request.rs +++ b/src/request.rs @@ -174,7 +174,7 @@ impl HttpRequest { /// let opt_t = req.conn_data::(); /// ``` /// - /// [on-connect]: crate::HttpServiceBuilder::on_connect_ext + /// [on-connect]: crate::HttpServer::on_connect pub fn conn_data(&self) -> Option<&T> { self.inner .conn_data From 406f69409550c8fc59dd4bde7be8d81f6fb65552 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 8 Dec 2021 06:01:11 +0000 Subject: [PATCH 07/87] standardize rustfmt max_width --- actix-files/src/files.rs | 2 +- actix-http/benches/status-line.rs | 12 +--- actix-http/benches/uninit-headers.rs | 9 +-- actix-http/examples/echo.rs | 5 +- actix-http/examples/echo2.rs | 3 +- actix-http/examples/hello-world.rs | 5 +- actix-http/examples/ws.rs | 5 +- actix-http/rustfmt.toml | 5 -- actix-http/src/body/body_stream.rs | 3 +- actix-http/src/body/boxed.rs | 4 +- actix-http/src/body/utils.rs | 5 +- actix-http/src/builder.rs | 3 +- actix-http/src/encoding/decoder.rs | 17 ++--- actix-http/src/encoding/encoder.rs | 6 +- actix-http/src/error.rs | 3 +- actix-http/src/h1/chunked.rs | 10 +-- actix-http/src/h1/decoder.rs | 9 +-- actix-http/src/h1/dispatcher.rs | 70 +++++++------------- actix-http/src/h1/encoder.rs | 25 ++----- actix-http/src/h1/payload.rs | 5 +- actix-http/src/h1/service.rs | 12 ++-- actix-http/src/h1/utils.rs | 21 +++--- actix-http/src/h2/dispatcher.rs | 9 +-- actix-http/src/h2/mod.rs | 5 +- actix-http/src/h2/service.rs | 6 +- actix-http/src/header/map.rs | 11 ++- actix-http/src/header/mod.rs | 32 +++++---- actix-http/src/header/shared/extended.rs | 15 ++--- actix-http/src/header/shared/http_date.rs | 3 +- actix-http/src/header/shared/quality_item.rs | 3 +- actix-http/src/lib.rs | 5 +- actix-http/src/payload.rs | 5 +- actix-http/src/response.rs | 4 +- actix-http/src/service.rs | 15 ++--- actix-http/src/ws/codec.rs | 12 +--- actix-http/src/ws/dispatcher.rs | 13 ++-- actix-http/src/ws/frame.rs | 11 +-- actix-http/src/ws/mask.rs | 8 +-- actix-http/src/ws/mod.rs | 4 +- actix-http/tests/test_client.rs | 4 +- actix-http/tests/test_openssl.rs | 25 +++---- actix-http/tests/test_rustls.rs | 18 ++--- actix-http/tests/test_server.rs | 52 ++++++--------- actix-http/tests/test_ws.rs | 9 ++- actix-router/src/resource.rs | 6 +- src/error/internal.rs | 2 +- src/error/mod.rs | 9 +-- src/response/builder.rs | 2 + 48 files changed, 192 insertions(+), 335 deletions(-) delete mode 100644 actix-http/rustfmt.toml diff --git a/actix-files/src/files.rs b/actix-files/src/files.rs index 06909bf08..d1dd6739d 100644 --- a/actix-files/src/files.rs +++ b/actix-files/src/files.rs @@ -262,9 +262,9 @@ impl Files { self } + /// See [`Files::method_guard`]. #[doc(hidden)] #[deprecated(since = "0.6.0", note = "Renamed to `method_guard`.")] - /// See [`Files::method_guard`]. pub fn use_guards(self, guard: G) -> Self { self.method_guard(guard) } diff --git a/actix-http/benches/status-line.rs b/actix-http/benches/status-line.rs index f62d18ed8..9fe099478 100644 --- a/actix-http/benches/status-line.rs +++ b/actix-http/benches/status-line.rs @@ -189,11 +189,7 @@ mod _original { n /= 100; curr -= 2; unsafe { - ptr::copy_nonoverlapping( - lut_ptr.offset(d1 as isize), - buf_ptr.offset(curr), - 2, - ); + ptr::copy_nonoverlapping(lut_ptr.offset(d1 as isize), buf_ptr.offset(curr), 2); } // decode last 1 or 2 chars @@ -206,11 +202,7 @@ mod _original { let d1 = n << 1; curr -= 2; unsafe { - ptr::copy_nonoverlapping( - lut_ptr.offset(d1 as isize), - buf_ptr.offset(curr), - 2, - ); + ptr::copy_nonoverlapping(lut_ptr.offset(d1 as isize), buf_ptr.offset(curr), 2); } } diff --git a/actix-http/benches/uninit-headers.rs b/actix-http/benches/uninit-headers.rs index 53a2528ab..5dfd3bc11 100644 --- a/actix-http/benches/uninit-headers.rs +++ b/actix-http/benches/uninit-headers.rs @@ -54,15 +54,10 @@ const EMPTY_HEADER_INDEX: HeaderIndex = HeaderIndex { value: (0, 0), }; -const EMPTY_HEADER_INDEX_ARRAY: [HeaderIndex; MAX_HEADERS] = - [EMPTY_HEADER_INDEX; MAX_HEADERS]; +const EMPTY_HEADER_INDEX_ARRAY: [HeaderIndex; MAX_HEADERS] = [EMPTY_HEADER_INDEX; MAX_HEADERS]; impl HeaderIndex { - fn record( - bytes: &[u8], - headers: &[httparse::Header<'_>], - indices: &mut [HeaderIndex], - ) { + fn record(bytes: &[u8], headers: &[httparse::Header<'_>], indices: &mut [HeaderIndex]) { let bytes_ptr = bytes.as_ptr() as usize; for (header, indices) in headers.iter().zip(indices.iter_mut()) { let name_start = header.name.as_ptr() as usize - bytes_ptr; diff --git a/actix-http/examples/echo.rs b/actix-http/examples/echo.rs index 5ff2bcc89..22f553f38 100644 --- a/actix-http/examples/echo.rs +++ b/actix-http/examples/echo.rs @@ -25,10 +25,7 @@ async fn main() -> io::Result<()> { Ok::<_, Error>( Response::build(StatusCode::OK) - .insert_header(( - "x-head", - HeaderValue::from_static("dummy value!"), - )) + .insert_header(("x-head", HeaderValue::from_static("dummy value!"))) .body(body), ) }) diff --git a/actix-http/examples/echo2.rs b/actix-http/examples/echo2.rs index 487b8d8d1..e3b915e05 100644 --- a/actix-http/examples/echo2.rs +++ b/actix-http/examples/echo2.rs @@ -1,8 +1,7 @@ use std::io; use actix_http::{ - body::MessageBody, header::HeaderValue, Error, HttpService, Request, Response, - StatusCode, + body::MessageBody, header::HeaderValue, Error, HttpService, Request, Response, StatusCode, }; use actix_server::Server; use bytes::BytesMut; diff --git a/actix-http/examples/hello-world.rs b/actix-http/examples/hello-world.rs index 3678774b8..0a46a89f9 100644 --- a/actix-http/examples/hello-world.rs +++ b/actix-http/examples/hello-world.rs @@ -17,10 +17,7 @@ async fn main() -> io::Result<()> { log::info!("{:?}", req); let mut res = Response::build(StatusCode::OK); - res.insert_header(( - "x-head", - HeaderValue::from_static("dummy value!"), - )); + res.insert_header(("x-head", HeaderValue::from_static("dummy value!"))); Ok::<_, Infallible>(res.body("Hello world!")) }) diff --git a/actix-http/examples/ws.rs b/actix-http/examples/ws.rs index b6be4d2f1..d70e43314 100644 --- a/actix-http/examples/ws.rs +++ b/actix-http/examples/ws.rs @@ -60,10 +60,7 @@ impl Heartbeat { impl Stream for Heartbeat { type Item = Result; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { log::trace!("poll"); ready!(self.as_mut().interval.poll_tick(cx)); diff --git a/actix-http/rustfmt.toml b/actix-http/rustfmt.toml deleted file mode 100644 index 5fcaaca0f..000000000 --- a/actix-http/rustfmt.toml +++ /dev/null @@ -1,5 +0,0 @@ -max_width = 89 -reorder_imports = true -#wrap_comments = true -#fn_args_density = "Compressed" -#use_small_heuristics = false diff --git a/actix-http/src/body/body_stream.rs b/actix-http/src/body/body_stream.rs index 1da7a848a..232d01590 100644 --- a/actix-http/src/body/body_stream.rs +++ b/actix-http/src/body/body_stream.rs @@ -165,8 +165,7 @@ mod tests { #[actix_rt::test] async fn stream_delayed_error() { - let body = - BodyStream::new(stream::iter(vec![Ok(Bytes::from("1")), Err(StreamErr)])); + let body = BodyStream::new(stream::iter(vec![Ok(Bytes::from("1")), Err(StreamErr)])); assert!(matches!(to_bytes(body).await, Err(StreamErr))); pin_project! { diff --git a/actix-http/src/body/boxed.rs b/actix-http/src/body/boxed.rs index 9442bd1df..c39da10c0 100644 --- a/actix-http/src/body/boxed.rs +++ b/actix-http/src/body/boxed.rs @@ -24,9 +24,7 @@ impl BoxBody { } /// Returns a mutable pinned reference to the inner message body type. - pub fn as_pin_mut( - &mut self, - ) -> Pin<&mut (dyn MessageBody>)> { + pub fn as_pin_mut(&mut self) -> Pin<&mut (dyn MessageBody>)> { self.0.as_mut() } } diff --git a/actix-http/src/body/utils.rs b/actix-http/src/body/utils.rs index a421ffd76..194af47f8 100644 --- a/actix-http/src/body/utils.rs +++ b/actix-http/src/body/utils.rs @@ -68,9 +68,8 @@ mod test { let bytes = to_bytes(body).await.unwrap(); assert_eq!(bytes, b"123"[..]); - let stream = - stream::iter(vec![Bytes::from_static(b"123"), Bytes::from_static(b"abc")]) - .map(Ok::<_, Error>); + let stream = stream::iter(vec![Bytes::from_static(b"123"), Bytes::from_static(b"abc")]) + .map(Ok::<_, Error>); let body = BodyStream::new(stream); let bytes = to_bytes(body).await.unwrap(); assert_eq!(bytes, b"123abc"[..]); diff --git a/actix-http/src/builder.rs b/actix-http/src/builder.rs index ca821f1d9..1b5da20b6 100644 --- a/actix-http/src/builder.rs +++ b/actix-http/src/builder.rs @@ -214,8 +214,7 @@ where self.local_addr, ); - H2Service::with_config(cfg, service.into_factory()) - .on_connect_ext(self.on_connect_ext) + H2Service::with_config(cfg, service.into_factory()).on_connect_ext(self.on_connect_ext) } /// Finish service configuration and create `HttpService` instance. diff --git a/actix-http/src/encoding/decoder.rs b/actix-http/src/encoding/decoder.rs index afe4c6e13..a46e330c9 100644 --- a/actix-http/src/encoding/decoder.rs +++ b/actix-http/src/encoding/decoder.rs @@ -44,17 +44,17 @@ where pub fn new(stream: S, encoding: ContentEncoding) -> Decoder { let decoder = match encoding { #[cfg(feature = "compress-brotli")] - ContentEncoding::Br => Some(ContentDecoder::Br(Box::new( - BrotliDecoder::new(Writer::new()), - ))), + ContentEncoding::Br => Some(ContentDecoder::Br(Box::new(BrotliDecoder::new( + Writer::new(), + )))), #[cfg(feature = "compress-gzip")] ContentEncoding::Deflate => Some(ContentDecoder::Deflate(Box::new( ZlibDecoder::new(Writer::new()), ))), #[cfg(feature = "compress-gzip")] - ContentEncoding::Gzip => Some(ContentDecoder::Gzip(Box::new( - GzDecoder::new(Writer::new()), - ))), + ContentEncoding::Gzip => Some(ContentDecoder::Gzip(Box::new(GzDecoder::new( + Writer::new(), + )))), #[cfg(feature = "compress-zstd")] ContentEncoding::Zstd => Some(ContentDecoder::Zstd(Box::new( ZstdDecoder::new(Writer::new()).expect( @@ -93,10 +93,7 @@ where { type Item = Result; - fn poll_next( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { if let Some(ref mut fut) = self.fut { let (chunk, decoder) = diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index 350e7f062..0886221cc 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -53,11 +53,7 @@ impl Encoder { } } - pub fn response( - encoding: ContentEncoding, - head: &mut ResponseHead, - body: B, - ) -> Self { + pub fn response(encoding: ContentEncoding, head: &mut ResponseHead, body: B) -> Self { let can_encode = !(head.headers().contains_key(&CONTENT_ENCODING) || head.status == StatusCode::SWITCHING_PROTOCOLS || head.status == StatusCode::NO_CONTENT diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index 231e90e57..a04867ae1 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -457,8 +457,7 @@ mod tests { #[test] fn test_payload_error() { - let err: PayloadError = - io::Error::new(io::ErrorKind::Other, "ParseError").into(); + let err: PayloadError = io::Error::new(io::ErrorKind::Other, "ParseError").into(); assert!(err.to_string().contains("ParseError")); let err = PayloadError::Incomplete(None); diff --git a/actix-http/src/h1/chunked.rs b/actix-http/src/h1/chunked.rs index e5b734fff..7d0532fcd 100644 --- a/actix-http/src/h1/chunked.rs +++ b/actix-http/src/h1/chunked.rs @@ -50,10 +50,7 @@ impl ChunkedState { } } - fn read_size( - rdr: &mut BytesMut, - size: &mut u64, - ) -> Poll> { + fn read_size(rdr: &mut BytesMut, size: &mut u64) -> Poll> { let radix = 16; let rem = match byte!(rdr) { @@ -111,10 +108,7 @@ impl ChunkedState { _ => Poll::Ready(Ok(ChunkedState::Extension)), // no supported extensions } } - fn read_size_lf( - rdr: &mut BytesMut, - size: u64, - ) -> Poll> { + fn read_size_lf(rdr: &mut BytesMut, size: u64) -> Poll> { match byte!(rdr) { b'\n' if size > 0 => Poll::Ready(Ok(ChunkedState::Body)), b'\n' if size == 0 => Poll::Ready(Ok(ChunkedState::EndCr)), diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index a4db19669..eb142f844 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -74,8 +74,7 @@ pub(crate) trait MessageType: Sized { let headers = self.headers_mut(); for idx in raw_headers.iter() { - let name = - HeaderName::from_bytes(&slice[idx.name.0..idx.name.1]).unwrap(); + let name = HeaderName::from_bytes(&slice[idx.name.0..idx.name.1]).unwrap(); // SAFETY: httparse already checks header value is only visible ASCII bytes // from_maybe_shared_unchecked contains debug assertions so they are omitted here @@ -605,8 +604,7 @@ mod tests { #[test] fn test_parse_body() { - let mut buf = - BytesMut::from("GET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody"); + let mut buf = BytesMut::from("GET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody"); let mut reader = MessageDecoder::::default(); let (req, pl) = reader.decode(&mut buf).unwrap().unwrap(); @@ -622,8 +620,7 @@ mod tests { #[test] fn test_parse_body_crlf() { - let mut buf = - BytesMut::from("\r\nGET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody"); + let mut buf = BytesMut::from("\r\nGET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody"); let mut reader = MessageDecoder::::default(); let (req, pl) = reader.decode(&mut buf).unwrap().unwrap(); diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index d2410be1e..64bf83e03 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -260,10 +260,7 @@ where } } - fn poll_flush( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let InnerDispatcherProj { io, write_buf, .. } = self.project(); let mut io = Pin::new(io.as_mut().unwrap()); @@ -273,10 +270,7 @@ where while written < len { match io.as_mut().poll_write(cx, &write_buf[written..])? { Poll::Ready(0) => { - return Poll::Ready(Err(io::Error::new( - io::ErrorKind::WriteZero, - "", - ))) + return Poll::Ready(Err(io::Error::new(io::ErrorKind::WriteZero, ""))) } Poll::Ready(n) => written += n, Poll::Pending => { @@ -419,15 +413,12 @@ where while this.write_buf.len() < super::payload::MAX_BUFFER_SIZE { match stream.as_mut().poll_next(cx) { Poll::Ready(Some(Ok(item))) => { - this.codec.encode( - Message::Chunk(Some(item)), - this.write_buf, - )?; + this.codec + .encode(Message::Chunk(Some(item)), this.write_buf)?; } Poll::Ready(None) => { - this.codec - .encode(Message::Chunk(None), this.write_buf)?; + this.codec.encode(Message::Chunk(None), this.write_buf)?; // payload stream finished. // set state to None and handle next message this.state.set(State::None); @@ -454,15 +445,12 @@ where while this.write_buf.len() < super::payload::MAX_BUFFER_SIZE { match stream.as_mut().poll_next(cx) { Poll::Ready(Some(Ok(item))) => { - this.codec.encode( - Message::Chunk(Some(item)), - this.write_buf, - )?; + this.codec + .encode(Message::Chunk(Some(item)), this.write_buf)?; } Poll::Ready(None) => { - this.codec - .encode(Message::Chunk(None), this.write_buf)?; + this.codec.encode(Message::Chunk(None), this.write_buf)?; // payload stream finished. // set state to None and handle next message this.state.set(State::None); @@ -568,9 +556,11 @@ where } }; } - _ => unreachable!( - "State must be set to ServiceCall or ExceptCall in handle_request" - ), + _ => { + unreachable!( + "State must be set to ServiceCall or ExceptCall in handle_request" + ) + } } } } @@ -604,8 +594,7 @@ where // everything remain in read buffer would be handed to // upgraded Request. MessageType::Stream if this.flow.upgrade.is_some() => { - this.messages - .push_back(DispatcherMessage::Upgrade(req)); + this.messages.push_back(DispatcherMessage::Upgrade(req)); break; } @@ -620,8 +609,7 @@ where where the state can be collected and consumed. */ let (ps, pl) = Payload::create(false); - let (req1, _) = - req.replace_payload(crate::Payload::H1(pl)); + let (req1, _) = req.replace_payload(crate::Payload::H1(pl)); req = req1; *this.payload = Some(ps); } @@ -642,9 +630,7 @@ where if let Some(ref mut payload) = this.payload { payload.feed_data(chunk); } else { - error!( - "Internal server error: unexpected payload chunk" - ); + error!("Internal server error: unexpected payload chunk"); this.flags.insert(Flags::READ_DISCONNECT); this.messages.push_back(DispatcherMessage::Error( Response::internal_server_error().drop_body(), @@ -682,12 +668,11 @@ where payload.set_error(PayloadError::Overflow); } // Requests overflow buffer size should be responded with 431 - this.messages.push_back(DispatcherMessage::Error( - Response::with_body( + this.messages + .push_back(DispatcherMessage::Error(Response::with_body( StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE, (), - ), - )); + ))); this.flags.insert(Flags::READ_DISCONNECT); *this.error = Some(ParseError::TooLarge.into()); break; @@ -729,8 +714,7 @@ where None => { // conditionally go into shutdown timeout if this.flags.contains(Flags::SHUTDOWN) { - if let Some(deadline) = this.codec.config().client_disconnect_timer() - { + if let Some(deadline) = this.codec.config().client_disconnect_timer() { // write client disconnect time out and poll again to // go into Some> branch this.ka_timer.set(Some(sleep_until(deadline))); @@ -773,9 +757,7 @@ where this.flags.insert(Flags::STARTED | Flags::SHUTDOWN); } // still have unfinished task. try to reset and register keep-alive. - } else if let Some(deadline) = - this.codec.config().keep_alive_expire() - { + } else if let Some(deadline) = this.codec.config().keep_alive_expire() { timer.as_mut().reset(deadline); let _ = timer.poll(cx); } @@ -1053,14 +1035,12 @@ mod tests { } fn ok_service( - ) -> impl Service, Error = Error> - { + ) -> impl Service, Error = Error> { fn_service(|_req: Request| ready(Ok::<_, Error>(Response::ok()))) } fn echo_path_service( - ) -> impl Service, Error = Error> - { + ) -> impl Service, Error = Error> { fn_service(|req: Request| { let path = req.path().as_bytes(); ready(Ok::<_, Error>( @@ -1069,8 +1049,8 @@ mod tests { }) } - fn echo_payload_service( - ) -> impl Service, Error = Error> { + fn echo_payload_service() -> impl Service, Error = Error> + { fn_service(|mut req: Request| { Box::pin(async move { use futures_util::stream::StreamExt as _; diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index fccd5da46..49bf5432d 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -103,9 +103,7 @@ pub(crate) trait MessageType: Sized { dst.put_slice(b"\r\n"); } } - BodySize::Sized(0) if camel_case => { - dst.put_slice(b"\r\nContent-Length: 0\r\n") - } + BodySize::Sized(0) if camel_case => dst.put_slice(b"\r\nContent-Length: 0\r\n"), BodySize::Sized(0) => dst.put_slice(b"\r\ncontent-length: 0\r\n"), BodySize::Sized(len) => helpers::write_content_length(len, dst), BodySize::None => dst.put_slice(b"\r\n"), @@ -307,11 +305,7 @@ impl MessageType for RequestHeadType { Version::HTTP_11 => "HTTP/1.1", Version::HTTP_2 => "HTTP/2.0", Version::HTTP_3 => "HTTP/3.0", - _ => - return Err(io::Error::new( - io::ErrorKind::Other, - "unsupported version" - )), + _ => return Err(io::Error::new(io::ErrorKind::Other, "unsupported version")), } ) .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) @@ -568,8 +562,7 @@ mod tests { ConnectionType::Close, &ServiceConfig::default(), ); - let data = - String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); + let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); assert!(data.contains("Content-Length: 0\r\n")); assert!(data.contains("Connection: close\r\n")); @@ -583,8 +576,7 @@ mod tests { ConnectionType::KeepAlive, &ServiceConfig::default(), ); - let data = - String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); + let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); assert!(data.contains("Transfer-Encoding: chunked\r\n")); assert!(data.contains("Content-Type: plain/text\r\n")); assert!(data.contains("Date: date\r\n")); @@ -605,8 +597,7 @@ mod tests { ConnectionType::KeepAlive, &ServiceConfig::default(), ); - let data = - String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); + let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); assert!(data.contains("transfer-encoding: chunked\r\n")); assert!(data.contains("content-type: xml\r\n")); assert!(data.contains("content-type: plain/text\r\n")); @@ -639,8 +630,7 @@ mod tests { ConnectionType::Close, &ServiceConfig::default(), ); - let data = - String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); + let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); assert!(data.contains("content-length: 0\r\n")); assert!(data.contains("connection: close\r\n")); assert!(data.contains("authorization: another authorization\r\n")); @@ -663,8 +653,7 @@ mod tests { ConnectionType::Upgrade, &ServiceConfig::default(), ); - let data = - String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); + let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); assert!(!data.contains("content-length: 0\r\n")); assert!(!data.contains("transfer-encoding: chunked\r\n")); } diff --git a/actix-http/src/h1/payload.rs b/actix-http/src/h1/payload.rs index cc771f28a..f912e0ba3 100644 --- a/actix-http/src/h1/payload.rs +++ b/actix-http/src/h1/payload.rs @@ -227,10 +227,7 @@ impl Inner { self.len } - fn readany( - &mut self, - cx: &mut Context<'_>, - ) -> Poll>> { + fn readany(&mut self, cx: &mut Context<'_>) -> Poll>> { if let Some(data) = self.items.pop_front() { self.len -= data.len(); self.need_read = self.len < MAX_BUFFER_SIZE; diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index fd9635690..c4e6e7714 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -266,8 +266,7 @@ where } } -impl ServiceFactory<(T, Option)> - for H1Service +impl ServiceFactory<(T, Option)> for H1Service where T: AsyncRead + AsyncWrite + Unpin + 'static, @@ -310,9 +309,9 @@ where let upgrade = match upgrade { Some(upgrade) => { - let upgrade = upgrade.await.map_err(|e| { - log::error!("Init http upgrade service error: {:?}", e) - })?; + let upgrade = upgrade + .await + .map_err(|e| log::error!("Init http upgrade service error: {:?}", e))?; Some(upgrade) } None => None, @@ -336,8 +335,7 @@ where /// `Service` implementation for HTTP/1 transport pub type H1ServiceHandler = HttpServiceHandler; -impl Service<(T, Option)> - for HttpServiceHandler +impl Service<(T, Option)> for HttpServiceHandler where T: AsyncRead + AsyncWrite + Unpin, diff --git a/actix-http/src/h1/utils.rs b/actix-http/src/h1/utils.rs index 905585a32..c8d79f0cd 100644 --- a/actix-http/src/h1/utils.rs +++ b/actix-http/src/h1/utils.rs @@ -70,15 +70,12 @@ where .unwrap() .is_write_buf_full() { - let next = - match this.body.as_mut().as_pin_mut().unwrap().poll_next(cx) { - Poll::Ready(Some(Ok(item))) => Poll::Ready(Some(item)), - Poll::Ready(Some(Err(err))) => { - return Poll::Ready(Err(err.into())) - } - Poll::Ready(None) => Poll::Ready(None), - Poll::Pending => Poll::Pending, - }; + let next = match this.body.as_mut().as_pin_mut().unwrap().poll_next(cx) { + Poll::Ready(Some(Ok(item))) => Poll::Ready(Some(item)), + Poll::Ready(Some(Err(err))) => return Poll::Ready(Err(err.into())), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + }; match next { Poll::Ready(item) => { @@ -88,9 +85,9 @@ where let _ = this.body.take(); } let framed = this.framed.as_mut().as_pin_mut().unwrap(); - framed.write(Message::Chunk(item)).map_err(|err| { - Error::new_send_response().with_cause(err) - })?; + framed + .write(Message::Chunk(item)) + .map_err(|err| Error::new_send_response().with_cause(err))?; } Poll::Pending => body_ready = false, } diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 55f71122b..da2d612f1 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -160,16 +160,11 @@ where Poll::Ready(_) => { ping_pong.on_flight = false; - let dead_line = - this.config.keep_alive_expire().unwrap(); + let dead_line = this.config.keep_alive_expire().unwrap(); ping_pong.timer.as_mut().reset(dead_line); } Poll::Pending => { - return ping_pong - .timer - .as_mut() - .poll(cx) - .map(|_| Ok(())) + return ping_pong.timer.as_mut().poll(cx).map(|_| Ok(())) } } } else { diff --git a/actix-http/src/h2/mod.rs b/actix-http/src/h2/mod.rs index 25d53403e..cbcb6d0fc 100644 --- a/actix-http/src/h2/mod.rs +++ b/actix-http/src/h2/mod.rs @@ -40,10 +40,7 @@ impl Payload { impl Stream for Payload { type Item = Result; - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.get_mut(); match ready!(Pin::new(&mut this.stream).poll_data(cx)) { diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index aa2a6cc69..f5821370a 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -10,8 +10,7 @@ use std::{ use actix_codec::{AsyncRead, AsyncWrite}; use actix_rt::net::TcpStream; use actix_service::{ - fn_factory, fn_service, IntoServiceFactory, Service, ServiceFactory, - ServiceFactoryExt as _, + fn_factory, fn_service, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt as _, }; use actix_utils::future::ready; use futures_core::{future::LocalBoxFuture, ready}; @@ -279,8 +278,7 @@ where } fn call(&self, (io, addr): (T, Option)) -> Self::Future { - let on_connect_data = - OnConnectData::from_io(&io, self.on_connect_ext.as_deref()); + let on_connect_data = OnConnectData::from_io(&io, self.on_connect_ext.as_deref()); H2ServiceHandlerResponse { state: State::Handshake( diff --git a/actix-http/src/header/map.rs b/actix-http/src/header/map.rs index 7b18be991..12c8f9462 100644 --- a/actix-http/src/header/map.rs +++ b/actix-http/src/header/map.rs @@ -123,12 +123,11 @@ impl HeaderMap { let mut map = HeaderMap::with_capacity(capacity); map.append(first_name.clone(), first_value); - let (map, _) = - drain.fold((map, first_name), |(mut map, prev_name), (name, value)| { - let name = name.unwrap_or(prev_name); - map.append(name.clone(), value); - (map, name) - }); + let (map, _) = drain.fold((map, first_name), |(mut map, prev_name), (name, value)| { + let name = name.unwrap_or(prev_name); + map.append(name.clone(), value); + (map, name) + }); map } diff --git a/actix-http/src/header/mod.rs b/actix-http/src/header/mod.rs index 381842e74..5fe76381b 100644 --- a/actix-http/src/header/mod.rs +++ b/actix-http/src/header/mod.rs @@ -11,22 +11,20 @@ pub use http::header::{ pub use http::header::{ ACCEPT, ACCEPT_CHARSET, ACCEPT_ENCODING, ACCEPT_LANGUAGE, ACCEPT_RANGES, ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS, - ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, - ACCESS_CONTROL_EXPOSE_HEADERS, ACCESS_CONTROL_MAX_AGE, - ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD, AGE, ALLOW, ALT_SVC, - AUTHORIZATION, CACHE_CONTROL, CONNECTION, CONTENT_DISPOSITION, CONTENT_ENCODING, - CONTENT_LANGUAGE, CONTENT_LENGTH, CONTENT_LOCATION, CONTENT_RANGE, - CONTENT_SECURITY_POLICY, CONTENT_SECURITY_POLICY_REPORT_ONLY, CONTENT_TYPE, COOKIE, - DATE, DNT, ETAG, EXPECT, EXPIRES, FORWARDED, FROM, HOST, IF_MATCH, - IF_MODIFIED_SINCE, IF_NONE_MATCH, IF_RANGE, IF_UNMODIFIED_SINCE, LAST_MODIFIED, - LINK, LOCATION, MAX_FORWARDS, ORIGIN, PRAGMA, PROXY_AUTHENTICATE, - PROXY_AUTHORIZATION, PUBLIC_KEY_PINS, PUBLIC_KEY_PINS_REPORT_ONLY, RANGE, REFERER, - REFERRER_POLICY, REFRESH, RETRY_AFTER, SEC_WEBSOCKET_ACCEPT, - SEC_WEBSOCKET_EXTENSIONS, SEC_WEBSOCKET_KEY, SEC_WEBSOCKET_PROTOCOL, + ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_EXPOSE_HEADERS, + ACCESS_CONTROL_MAX_AGE, ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD, AGE, + ALLOW, ALT_SVC, AUTHORIZATION, CACHE_CONTROL, CONNECTION, CONTENT_DISPOSITION, + CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LENGTH, CONTENT_LOCATION, CONTENT_RANGE, + CONTENT_SECURITY_POLICY, CONTENT_SECURITY_POLICY_REPORT_ONLY, CONTENT_TYPE, COOKIE, DATE, + DNT, ETAG, EXPECT, EXPIRES, FORWARDED, FROM, HOST, IF_MATCH, IF_MODIFIED_SINCE, + IF_NONE_MATCH, IF_RANGE, IF_UNMODIFIED_SINCE, LAST_MODIFIED, LINK, LOCATION, MAX_FORWARDS, + ORIGIN, PRAGMA, PROXY_AUTHENTICATE, PROXY_AUTHORIZATION, PUBLIC_KEY_PINS, + PUBLIC_KEY_PINS_REPORT_ONLY, RANGE, REFERER, REFERRER_POLICY, REFRESH, RETRY_AFTER, + SEC_WEBSOCKET_ACCEPT, SEC_WEBSOCKET_EXTENSIONS, SEC_WEBSOCKET_KEY, SEC_WEBSOCKET_PROTOCOL, SEC_WEBSOCKET_VERSION, SERVER, SET_COOKIE, STRICT_TRANSPORT_SECURITY, TE, TRAILER, - TRANSFER_ENCODING, UPGRADE, UPGRADE_INSECURE_REQUESTS, USER_AGENT, VARY, VIA, - WARNING, WWW_AUTHENTICATE, X_CONTENT_TYPE_OPTIONS, X_DNS_PREFETCH_CONTROL, - X_FRAME_OPTIONS, X_XSS_PROTECTION, + TRANSFER_ENCODING, UPGRADE, UPGRADE_INSECURE_REQUESTS, USER_AGENT, VARY, VIA, WARNING, + WWW_AUTHENTICATE, X_CONTENT_TYPE_OPTIONS, X_DNS_PREFETCH_CONTROL, X_FRAME_OPTIONS, + X_XSS_PROTECTION, }; use crate::{error::ParseError, HttpMessage}; @@ -43,8 +41,8 @@ pub use self::into_pair::IntoHeaderPair; pub use self::into_value::IntoHeaderValue; pub use self::map::HeaderMap; pub use self::shared::{ - parse_extended_value, q, Charset, ContentEncoding, ExtendedValue, HttpDate, - LanguageTag, Quality, QualityItem, + parse_extended_value, q, Charset, ContentEncoding, ExtendedValue, HttpDate, LanguageTag, + Quality, QualityItem, }; pub use self::utils::{ fmt_comma_delimited, from_comma_delimited, from_one_raw_str, http_percent_encode, diff --git a/actix-http/src/header/shared/extended.rs b/actix-http/src/header/shared/extended.rs index 60f2d359e..1af9ca20e 100644 --- a/actix-http/src/header/shared/extended.rs +++ b/actix-http/src/header/shared/extended.rs @@ -63,9 +63,7 @@ pub struct ExtendedValue { /// [RFC 2231 §7]: https://datatracker.ietf.org/doc/html/rfc2231#section-7 /// [RFC 2978 §2.3]: https://datatracker.ietf.org/doc/html/rfc2978#section-2.3 /// [RFC 3986 §2.1]: https://datatracker.ietf.org/doc/html/rfc5646#section-2.1 -pub fn parse_extended_value( - val: &str, -) -> Result { +pub fn parse_extended_value(val: &str) -> Result { // Break into three pieces separated by the single-quote character let mut parts = val.splitn(3, '\''); @@ -100,8 +98,7 @@ pub fn parse_extended_value( impl fmt::Display for ExtendedValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let encoded_value = - percent_encoding::percent_encode(&self.value[..], HTTP_VALUE); + let encoded_value = percent_encoding::percent_encode(&self.value[..], HTTP_VALUE); if let Some(ref lang) = self.language_tag { write!(f, "{}'{}'{}", self.charset, lang, encoded_value) } else { @@ -143,8 +140,8 @@ mod tests { assert!(extended_value.language_tag.is_none()); assert_eq!( vec![ - 194, 163, b' ', b'a', b'n', b'd', b' ', 226, 130, 172, b' ', b'r', b'a', - b't', b'e', b's', + 194, 163, b' ', b'a', b'n', b'd', b' ', 226, 130, 172, b' ', b'r', b'a', b't', + b'e', b's', ], extended_value.value ); @@ -185,8 +182,8 @@ mod tests { charset: Charset::Ext("UTF-8".to_string()), language_tag: None, value: vec![ - 194, 163, b' ', b'a', b'n', b'd', b' ', 226, 130, 172, b' ', b'r', b'a', - b't', b'e', b's', + 194, 163, b' ', b'a', b'n', b'd', b' ', 226, 130, 172, b' ', b'r', b'a', b't', + b'e', b's', ], }; assert_eq!( diff --git a/actix-http/src/header/shared/http_date.rs b/actix-http/src/header/shared/http_date.rs index 8dbdf4a62..228f6f00e 100644 --- a/actix-http/src/header/shared/http_date.rs +++ b/actix-http/src/header/shared/http_date.rs @@ -4,8 +4,7 @@ use bytes::BytesMut; use http::header::{HeaderValue, InvalidHeaderValue}; use crate::{ - config::DATE_VALUE_LENGTH, error::ParseError, header::IntoHeaderValue, - helpers::MutWriter, + config::DATE_VALUE_LENGTH, error::ParseError, header::IntoHeaderValue, helpers::MutWriter, }; /// A timestamp with HTTP-style formatting and parsing. diff --git a/actix-http/src/header/shared/quality_item.rs b/actix-http/src/header/shared/quality_item.rs index 9354915ad..c9eee7d9d 100644 --- a/actix-http/src/header/shared/quality_item.rs +++ b/actix-http/src/header/shared/quality_item.rs @@ -120,8 +120,7 @@ impl str::FromStr for QualityItem { } let q_value = q_val.parse::().map_err(|_| ParseError::Header)?; - let q_value = - Quality::try_from(q_value).map_err(|_| ParseError::Header)?; + let q_value = Quality::try_from(q_value).map_err(|_| ParseError::Header)?; quality = q_value; raw_item = val; diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index 89ee139c0..19c66d155 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -87,10 +87,7 @@ pub(crate) struct OnConnectData(Option); impl OnConnectData { /// Construct by calling the on-connect callback with the underlying transport I/O. - pub(crate) fn from_io( - io: &T, - on_connect_ext: Option<&ConnectCallback>, - ) -> Self { + pub(crate) fn from_io(io: &T, on_connect_ext: Option<&ConnectCallback>) -> Self { let ext = on_connect_ext.map(|handler| { let mut extensions = Extensions::default(); handler(io, &mut extensions); diff --git a/actix-http/src/payload.rs b/actix-http/src/payload.rs index 54de6ed93..85bfc0b5a 100644 --- a/actix-http/src/payload.rs +++ b/actix-http/src/payload.rs @@ -56,10 +56,7 @@ where type Item = Result; #[inline] - fn poll_next( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match self.get_mut() { Payload::None => Poll::Ready(None), Payload::H1(ref mut pl) => pl.readany(cx), diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index ee7e38913..861cab2cb 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -231,9 +231,7 @@ impl Default for Response { } } -impl>, E: Into> From> - for Response -{ +impl>, E: Into> From> for Response { fn from(res: Result) -> Self { match res { Ok(val) => val.into(), diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index cba4c1756..93168749d 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -161,11 +161,7 @@ where X::Error: Into>, X::InitError: fmt::Debug, - U: ServiceFactory< - (Request, Framed), - Config = (), - Response = (), - >, + U: ServiceFactory<(Request, Framed), Config = (), Response = ()>, U::Future: 'static, U::Error: fmt::Display + Into>, U::InitError: fmt::Debug, @@ -381,9 +377,9 @@ where let upgrade = match upgrade { Some(upgrade) => { - let upgrade = upgrade.await.map_err(|e| { - log::error!("Init http upgrade service error: {:?}", e) - })?; + let upgrade = upgrade + .await + .map_err(|e| log::error!("Init http upgrade service error: {:?}", e))?; Some(upgrade) } None => None, @@ -626,8 +622,7 @@ where StateProj::H2Handshake { handshake: data } => { match ready!(Pin::new(&mut data.as_mut().unwrap().0).poll(cx)) { Ok((conn, timer)) => { - let (_, config, flow, conn_data, peer_addr) = - data.take().unwrap(); + let (_, config, flow, conn_data, peer_addr) = data.take().unwrap(); self.as_mut().project().state.set(State::H2 { dispatcher: h2::Dispatcher::new( diff --git a/actix-http/src/ws/codec.rs b/actix-http/src/ws/codec.rs index d80613e5f..f5b755eec 100644 --- a/actix-http/src/ws/codec.rs +++ b/actix-http/src/ws/codec.rs @@ -224,9 +224,7 @@ impl Decoder for Codec { OpCode::Continue => { if self.flags.contains(Flags::CONTINUATION) { Ok(Some(Frame::Continuation(Item::Continue( - payload - .map(|pl| pl.freeze()) - .unwrap_or_else(Bytes::new), + payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new), )))) } else { Err(ProtocolError::ContinuationNotStarted) @@ -236,9 +234,7 @@ impl Decoder for Codec { if !self.flags.contains(Flags::CONTINUATION) { self.flags.insert(Flags::CONTINUATION); Ok(Some(Frame::Continuation(Item::FirstBinary( - payload - .map(|pl| pl.freeze()) - .unwrap_or_else(Bytes::new), + payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new), )))) } else { Err(ProtocolError::ContinuationStarted) @@ -248,9 +244,7 @@ impl Decoder for Codec { if !self.flags.contains(Flags::CONTINUATION) { self.flags.insert(Flags::CONTINUATION); Ok(Some(Frame::Continuation(Item::FirstText( - payload - .map(|pl| pl.freeze()) - .unwrap_or_else(Bytes::new), + payload.map(|pl| pl.freeze()).unwrap_or_else(Bytes::new), )))) } else { Err(ProtocolError::ContinuationStarted) diff --git a/actix-http/src/ws/dispatcher.rs b/actix-http/src/ws/dispatcher.rs index a3f766e9c..f12ae1b1a 100644 --- a/actix-http/src/ws/dispatcher.rs +++ b/actix-http/src/ws/dispatcher.rs @@ -304,8 +304,7 @@ mod inner { let item = match this.framed.next_item(cx) { Poll::Ready(Some(Ok(el))) => el, Poll::Ready(Some(Err(err))) => { - *this.state = - State::FramedError(DispatcherError::Decoder(err)); + *this.state = State::FramedError(DispatcherError::Decoder(err)); return true; } Poll::Pending => return false, @@ -348,8 +347,7 @@ mod inner { match Pin::new(&mut this.rx).poll_next(cx) { Poll::Ready(Some(Ok(Message::Item(msg)))) => { if let Err(err) = this.framed.as_mut().write(msg) { - *this.state = - State::FramedError(DispatcherError::Encoder(err)); + *this.state = State::FramedError(DispatcherError::Encoder(err)); return true; } } @@ -371,8 +369,7 @@ mod inner { Poll::Ready(Ok(_)) => {} Poll::Ready(Err(err)) => { debug!("Error sending data: {:?}", err); - *this.state = - State::FramedError(DispatcherError::Encoder(err)); + *this.state = State::FramedError(DispatcherError::Encoder(err)); return true; } } @@ -432,9 +429,7 @@ mod inner { Poll::Ready(Ok(())) } } - State::FramedError(_) => { - Poll::Ready(Err(this.state.take_framed_error())) - } + State::FramedError(_) => Poll::Ready(Err(this.state.take_framed_error())), State::Stopping => Poll::Ready(Ok(())), }; } diff --git a/actix-http/src/ws/frame.rs b/actix-http/src/ws/frame.rs index 46edf5d85..b58ef7362 100644 --- a/actix-http/src/ws/frame.rs +++ b/actix-http/src/ws/frame.rs @@ -16,8 +16,7 @@ impl Parser { src: &[u8], server: bool, max_size: usize, - ) -> Result)>, ProtocolError> - { + ) -> Result)>, ProtocolError> { let chunk_len = src.len(); let mut idx = 2; @@ -228,15 +227,11 @@ mod tests { payload: Bytes, } - fn is_none( - frm: &Result)>, ProtocolError>, - ) -> bool { + fn is_none(frm: &Result)>, ProtocolError>) -> bool { matches!(*frm, Ok(None)) } - fn extract( - frm: Result)>, ProtocolError>, - ) -> F { + fn extract(frm: Result)>, ProtocolError>) -> F { match frm { Ok(Some((finished, opcode, payload))) => F { finished, diff --git a/actix-http/src/ws/mask.rs b/actix-http/src/ws/mask.rs index 11a6ddc32..20b4372a0 100644 --- a/actix-http/src/ws/mask.rs +++ b/actix-http/src/ws/mask.rs @@ -54,8 +54,8 @@ mod tests { let mask = [0x6d, 0xb6, 0xb2, 0x80]; let unmasked = vec![ - 0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17, - 0x74, 0xf9, 0x12, 0x03, + 0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17, 0x74, 0xf9, + 0x12, 0x03, ]; // Check masking with proper alignment. @@ -85,8 +85,8 @@ mod tests { fn test_apply_mask() { let mask = [0x6d, 0xb6, 0xb2, 0x80]; let unmasked = vec![ - 0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17, - 0x74, 0xf9, 0x12, 0x03, + 0xf3, 0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x82, 0xff, 0xfe, 0x00, 0x17, 0x74, 0xf9, + 0x12, 0x03, ]; for data_len in 0..=unmasked.len() { diff --git a/actix-http/src/ws/mod.rs b/actix-http/src/ws/mod.rs index cb1aa6730..c23d4edfc 100644 --- a/actix-http/src/ws/mod.rs +++ b/actix-http/src/ws/mod.rs @@ -9,9 +9,7 @@ use derive_more::{Display, Error, From}; use http::{header, Method, StatusCode}; use crate::body::BoxBody; -use crate::{ - header::HeaderValue, message::RequestHead, response::Response, ResponseBuilder, -}; +use crate::{header::HeaderValue, message::RequestHead, response::Response, ResponseBuilder}; mod codec; mod dispatcher; diff --git a/actix-http/tests/test_client.rs b/actix-http/tests/test_client.rs index acbdc8e83..a3adcdfd6 100644 --- a/actix-http/tests/test_client.rs +++ b/actix-http/tests/test_client.rs @@ -1,8 +1,6 @@ use std::convert::Infallible; -use actix_http::{ - body::BoxBody, HttpMessage, HttpService, Request, Response, StatusCode, -}; +use actix_http::{body::BoxBody, HttpMessage, HttpService, Request, Response, StatusCode}; use actix_http_test::test_server; use actix_service::ServiceFactoryExt; use actix_utils::future; diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index 8ba41b4bd..0c373b8b2 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -170,10 +170,11 @@ async fn test_h2_headers() { let mut srv = test_server(move || { let data = data.clone(); - HttpService::build().h2(move |_| { - let mut builder = Response::build(StatusCode::OK); - for idx in 0..90 { - builder.insert_header( + HttpService::build() + .h2(move |_| { + let mut builder = Response::build(StatusCode::OK); + for idx in 0..90 { + builder.insert_header( (format!("X-TEST-{}", idx).as_str(), "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ @@ -189,12 +190,13 @@ async fn test_h2_headers() { TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", )); - } - ok::<_, Infallible>(builder.body(data.clone())) - }) + } + ok::<_, Infallible>(builder.body(data.clone())) + }) .openssl(tls_config()) - .map_err(|_| ()) - }).await; + .map_err(|_| ()) + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -315,9 +317,8 @@ async fn test_h2_body_length() { let mut srv = test_server(move || { HttpService::build() .h2(|_| async { - let body = once(async { - Ok::<_, Infallible>(Bytes::from_static(STR.as_ref())) - }); + let body = + once(async { Ok::<_, Infallible>(Bytes::from_static(STR.as_ref())) }); Ok::<_, Infallible>( Response::ok().set_body(SizedStream::new(STR.len() as u64, body)), diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index 1fc3bdf49..42ff0dba1 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -238,10 +238,11 @@ async fn test_h2_headers() { let mut srv = test_server(move || { let data = data.clone(); - HttpService::build().h2(move |_| { - let mut config = Response::build(StatusCode::OK); - for idx in 0..90 { - config.insert_header(( + HttpService::build() + .h2(move |_| { + let mut config = Response::build(StatusCode::OK); + for idx in 0..90 { + config.insert_header(( format!("X-TEST-{}", idx).as_str(), "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ @@ -257,11 +258,12 @@ async fn test_h2_headers() { TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", )); - } - ok::<_, Infallible>(config.body(data.clone())) - }) + } + ok::<_, Infallible>(config.body(data.clone())) + }) .rustls(tls_config()) - }).await; + }) + .await; let response = srv.sget("/").send().await.unwrap(); assert!(response.status().is_success()); diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index b7fde877f..1bb574fd6 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -154,9 +154,7 @@ async fn test_chunked_payload() { }) .fold(0usize, |acc, chunk| ready(acc + chunk.len())) .map(|req_size| { - Ok::<_, Error>( - Response::ok().set_body(format!("size={}", req_size)), - ) + Ok::<_, Error>(Response::ok().set_body(format!("size={}", req_size))) }) })) .tcp() @@ -165,8 +163,7 @@ async fn test_chunked_payload() { let returned_size = { let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream - .write_all(b"POST /test HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"); + let _ = stream.write_all(b"POST /test HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"); for chunk_size in chunk_sizes.iter() { let mut bytes = Vec::new(); @@ -293,8 +290,7 @@ async fn test_http1_keepalive_close() { .await; let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = - stream.write_all(b"GET /test/tests/test HTTP/1.1\r\nconnection: close\r\n\r\n"); + let _ = stream.write_all(b"GET /test/tests/test HTTP/1.1\r\nconnection: close\r\n\r\n"); let mut data = vec![0; 1024]; let _ = stream.read(&mut data); assert_eq!(&data[..17], b"HTTP/1.1 200 OK\r\n"); @@ -338,8 +334,8 @@ async fn test_http10_keepalive() { .await; let mut stream = net::TcpStream::connect(srv.addr()).unwrap(); - let _ = stream - .write_all(b"GET /test/tests/test HTTP/1.0\r\nconnection: keep-alive\r\n\r\n"); + let _ = + stream.write_all(b"GET /test/tests/test HTTP/1.0\r\nconnection: keep-alive\r\n\r\n"); let mut data = vec![0; 1024]; let _ = stream.read(&mut data); assert_eq!(&data[..17], b"HTTP/1.0 200 OK\r\n"); @@ -436,10 +432,11 @@ async fn test_h1_headers() { let mut srv = test_server(move || { let data = data.clone(); - HttpService::build().h1(move |_| { - let mut builder = Response::build(StatusCode::OK); - for idx in 0..90 { - builder.insert_header(( + HttpService::build() + .h1(move |_| { + let mut builder = Response::build(StatusCode::OK); + for idx in 0..90 { + builder.insert_header(( format!("X-TEST-{}", idx).as_str(), "TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ @@ -455,10 +452,12 @@ async fn test_h1_headers() { TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST \ TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ", )); - } - ok::<_, Infallible>(builder.body(data.clone())) - }).tcp() - }).await; + } + ok::<_, Infallible>(builder.body(data.clone())) + }) + .tcp() + }) + .await; let response = srv.get("/").send().await.unwrap(); assert!(response.status().is_success()); @@ -655,9 +654,7 @@ async fn test_h1_body_chunked_implicit() { HttpService::build() .h1(|_| { let body = once(ok::<_, Error>(Bytes::from_static(STR.as_ref()))); - ok::<_, Infallible>( - Response::build(StatusCode::OK).body(BodyStream::new(body)), - ) + ok::<_, Infallible>(Response::build(StatusCode::OK).body(BodyStream::new(body))) }) .tcp() }) @@ -776,10 +773,8 @@ async fn test_not_modified_spec_h1() { .h1(|req: Request| { let res: Response = match req.path() { // with no content-length - "/none" => { - Response::with_body(StatusCode::NOT_MODIFIED, body::None::new()) - .map_into_boxed_body() - } + "/none" => Response::with_body(StatusCode::NOT_MODIFIED, body::None::new()) + .map_into_boxed_body(), // with no content-length "/body" => Response::with_body(StatusCode::NOT_MODIFIED, "1234") @@ -787,10 +782,8 @@ async fn test_not_modified_spec_h1() { // with manual content-length header and specific None body "/cl-none" => { - let mut res = Response::with_body( - StatusCode::NOT_MODIFIED, - body::None::new(), - ); + let mut res = + Response::with_body(StatusCode::NOT_MODIFIED, body::None::new()); res.headers_mut() .insert(CL.clone(), header::HeaderValue::from_static("24")); res.map_into_boxed_body() @@ -798,8 +791,7 @@ async fn test_not_modified_spec_h1() { // with manual content-length header and ignore-able body "/cl-body" => { - let mut res = - Response::with_body(StatusCode::NOT_MODIFIED, "1234"); + let mut res = Response::with_body(StatusCode::NOT_MODIFIED, "1234"); res.headers_mut() .insert(CL.clone(), header::HeaderValue::from_static("4")); res.map_into_boxed_body() diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index c91382013..ed8c61fd6 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -56,8 +56,9 @@ impl From for Response { WsServiceError::Http(err) => err.into(), WsServiceError::Ws(err) => err.into(), WsServiceError::Io(_err) => unreachable!(), - WsServiceError::Dispatcher => Response::internal_server_error() - .set_body(BoxBody::new(format!("{}", err))), + WsServiceError::Dispatcher => { + Response::internal_server_error().set_body(BoxBody::new(format!("{}", err))) + } } } } @@ -97,9 +98,7 @@ where async fn service(msg: Frame) -> Result { let msg = match msg { Frame::Ping(msg) => Message::Pong(msg), - Frame::Text(text) => { - Message::Text(String::from_utf8_lossy(&text).into_owned().into()) - } + Frame::Text(text) => Message::Text(String::from_utf8_lossy(&text).into_owned().into()), Frame::Binary(bin) => Message::Binary(bin), Frame::Continuation(item) => Message::Continuation(item), Frame::Close(reason) => Message::Close(reason), diff --git a/actix-router/src/resource.rs b/actix-router/src/resource.rs index d5f738a05..fa77b1e7b 100644 --- a/actix-router/src/resource.rs +++ b/actix-router/src/resource.rs @@ -168,7 +168,7 @@ const REGEX_FLAGS: &str = "(?s-m)"; /// extracted in the same way as non-tail dynamic segments. /// /// ## Examples -/// ```rust +/// ``` /// # use actix_router::{Path, ResourceDef}; /// let resource = ResourceDef::new("/blob/{tail}*"); /// assert!(resource.is_match("/blob/HEAD/Cargo.toml")); @@ -191,7 +191,7 @@ const REGEX_FLAGS: &str = "(?s-m)"; /// expectations in the router using these definitions and cause runtime panics. /// /// ## Examples -/// ```rust +/// ``` /// # use actix_router::ResourceDef; /// let resource = ResourceDef::new(["/home", "/index"]); /// assert!(resource.is_match("/home")); @@ -206,7 +206,7 @@ const REGEX_FLAGS: &str = "(?s-m)"; /// resource-path pairs that would not be compatible. /// /// ## Examples -/// ```rust +/// ``` /// # use actix_router::ResourceDef; /// assert!(!ResourceDef::new("/root").is_match("/root/")); /// assert!(!ResourceDef::new("/root/").is_match("/root")); diff --git a/src/error/internal.rs b/src/error/internal.rs index c766ba83e..b8e169018 100644 --- a/src/error/internal.rs +++ b/src/error/internal.rs @@ -128,7 +128,7 @@ macro_rules! error_helper { InternalError::new(err, StatusCode::$status).into() } } - } + }; } error_helper!(ErrorBadRequest, BAD_REQUEST); diff --git a/src/error/mod.rs b/src/error/mod.rs index 48f71618c..4877358a4 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -1,8 +1,9 @@ //! Error and Result module - -/// This is meant to be a glob import of the whole error module, but rustdoc can't handle -/// shadowing `Error` type, so it is expanded manually. -/// See +// This is meant to be a glob import of the whole error module except for `Error`. Rustdoc can't yet +// correctly resolve the conflicting `Error` type defined in this module, so these re-exports are +// expanded manually. +// +// See pub use actix_http::error::{ BlockingError, ContentTypeError, DispatchError, HttpError, ParseError, PayloadError, }; diff --git a/src/response/builder.rs b/src/response/builder.rs index 50e23f81b..18a1c8a7f 100644 --- a/src/response/builder.rs +++ b/src/response/builder.rs @@ -109,6 +109,7 @@ impl HttpResponseBuilder { } /// Replaced with [`Self::insert_header()`]. + #[doc(hidden)] #[deprecated( since = "4.0.0", note = "Replaced with `insert_header((key, value))`. Will be removed in v5." @@ -133,6 +134,7 @@ impl HttpResponseBuilder { } /// Replaced with [`Self::append_header()`]. + #[doc(hidden)] #[deprecated( since = "4.0.0", note = "Replaced with `append_header((key, value))`. Will be removed in v5." From 07f2fe385b1845eca1599904da9476487e7999f5 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 8 Dec 2021 06:09:56 +0000 Subject: [PATCH 08/87] standardize crate level lints --- actix-files/src/lib.rs | 4 ++-- actix-http-test/src/lib.rs | 3 ++- actix-http/src/lib.rs | 3 ++- actix-multipart/src/lib.rs | 3 ++- actix-router/src/lib.rs | 1 + actix-test/src/lib.rs | 3 +++ actix-web-actors/src/lib.rs | 4 ++-- actix-web-codegen/src/lib.rs | 2 ++ actix-web-codegen/src/route.rs | 5 +---- awc/src/lib.rs | 3 ++- src/lib.rs | 1 + 11 files changed, 20 insertions(+), 12 deletions(-) diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 3af5282f1..6408e02da 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -11,8 +11,8 @@ //! .service(Files::new("/static", ".").prefer_utf8(true)); //! ``` -#![deny(rust_2018_idioms)] -#![warn(missing_docs, missing_debug_implementations)] +#![deny(rust_2018_idioms, nonstandard_style)] +#![warn(future_incompatible, missing_docs, missing_debug_implementations)] use actix_service::boxed::{BoxService, BoxServiceFactory}; use actix_web::{ diff --git a/actix-http-test/src/lib.rs b/actix-http-test/src/lib.rs index ff86e565a..e7e479ab2 100644 --- a/actix-http-test/src/lib.rs +++ b/actix-http-test/src/lib.rs @@ -1,6 +1,7 @@ //! Various helpers for Actix applications to use during testing. -#![deny(rust_2018_idioms)] +#![deny(rust_2018_idioms, nonstandard_style)] +#![warn(future_incompatible)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index 19c66d155..60dc26f0f 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -14,7 +14,8 @@ //! [rustls]: https://crates.io/crates/rustls //! [trust-dns]: https://crates.io/crates/trust-dns -#![deny(rust_2018_idioms, nonstandard_style, clippy::uninit_assumed_init)] +#![deny(rust_2018_idioms, nonstandard_style)] +#![warn(future_incompatible)] #![allow( clippy::type_complexity, clippy::too_many_arguments, diff --git a/actix-multipart/src/lib.rs b/actix-multipart/src/lib.rs index 38a24e28f..3d536e08d 100644 --- a/actix-multipart/src/lib.rs +++ b/actix-multipart/src/lib.rs @@ -1,6 +1,7 @@ //! Multipart form support for Actix Web. -#![deny(rust_2018_idioms)] +#![deny(rust_2018_idioms, nonstandard_style)] +#![warn(future_incompatible)] #![allow(clippy::borrow_interior_mutable_const)] mod error; diff --git a/actix-router/src/lib.rs b/actix-router/src/lib.rs index 463e59e42..f616f7fc6 100644 --- a/actix-router/src/lib.rs +++ b/actix-router/src/lib.rs @@ -1,6 +1,7 @@ //! Resource path matching and router. #![deny(rust_2018_idioms, nonstandard_style)] +#![warn(future_incompatible)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] diff --git a/actix-test/src/lib.rs b/actix-test/src/lib.rs index 7e493ce71..934b8f3aa 100644 --- a/actix-test/src/lib.rs +++ b/actix-test/src/lib.rs @@ -26,6 +26,9 @@ //! } //! ``` +#![deny(rust_2018_idioms, nonstandard_style)] +#![warn(future_incompatible)] + #[cfg(feature = "openssl")] extern crate tls_openssl as openssl; #[cfg(feature = "rustls")] diff --git a/actix-web-actors/src/lib.rs b/actix-web-actors/src/lib.rs index 7a4823d91..70c957020 100644 --- a/actix-web-actors/src/lib.rs +++ b/actix-web-actors/src/lib.rs @@ -1,7 +1,7 @@ //! Actix actors support for Actix Web. -#![deny(rust_2018_idioms)] -#![allow(clippy::borrow_interior_mutable_const)] +#![deny(rust_2018_idioms, nonstandard_style)] +#![warn(future_incompatible)] mod context; pub mod ws; diff --git a/actix-web-codegen/src/lib.rs b/actix-web-codegen/src/lib.rs index cebf9e5fb..52cfc0d8f 100644 --- a/actix-web-codegen/src/lib.rs +++ b/actix-web-codegen/src/lib.rs @@ -57,6 +57,8 @@ //! [DELETE]: macro@delete #![recursion_limit = "512"] +#![deny(rust_2018_idioms, nonstandard_style)] +#![warn(future_incompatible)] use proc_macro::TokenStream; use quote::quote; diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index eac1948a7..a4472efd2 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -1,7 +1,4 @@ -extern crate proc_macro; - -use std::collections::HashSet; -use std::convert::TryFrom; +use std::{collections::HashSet, convert::TryFrom}; use actix_router::ResourceDef; use proc_macro::TokenStream; diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 0cb6c7f4f..06fd33fac 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -95,7 +95,8 @@ //! # } //! ``` -#![deny(rust_2018_idioms)] +#![deny(rust_2018_idioms, nonstandard_style)] +#![warn(future_incompatible)] #![allow( clippy::type_complexity, clippy::borrow_interior_mutable_const, diff --git a/src/lib.rs b/src/lib.rs index f6ec4082a..a44c9b3fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,6 +65,7 @@ //! * `secure-cookies` - secure cookies support #![deny(rust_2018_idioms, nonstandard_style)] +#![warn(future_incompatible)] #![allow(clippy::needless_doctest_main, clippy::type_complexity)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] From 7dc034f0fb70846d9bb3445a2414a142356892e1 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 8 Dec 2021 22:58:50 +0000 Subject: [PATCH 09/87] Remove extensions from head (#2487) --- CHANGES.md | 6 +++++ actix-http/CHANGES.md | 3 +++ actix-http/examples/hello-world.rs | 16 ++++++++++--- actix-http/src/extensions.rs | 2 +- actix-http/src/message.rs | 17 +------------ actix-http/src/request.rs | 26 +++++++++++++------- src/app_service.rs | 3 +++ src/info.rs | 12 ++-------- src/request.rs | 38 ++++++++++++++++-------------- src/request_data.rs | 9 ++++--- src/service.rs | 2 +- src/test.rs | 27 ++++++++++++++++++--- 12 files changed, 96 insertions(+), 65 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2ef1478dc..365c89af9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ * `HttpResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] * `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] * Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491] +* `HttpRequest::{req_data,req_data_mut}`. [#2487] ### Changed * Rename `Accept::{mime_precedence => ranked}`. [#2480] @@ -16,18 +17,23 @@ * `HttpRequest::url_for` no longer constructs URLs with query or fragment components. [#2430] * Remove `B` (body) type parameter on `App`. [#2493] * Add `B` (body) type parameter on `Scope`. [#2492] +* Request-local data container is no longer part of a `RequestHead`. Instead it is a distinct part of a `Request`. [#2487] ### Fixed * Accept wildcard `*` items in `AcceptLanguage`. [#2480] * Re-exports `dev::{BodySize, MessageBody, SizedStream}`. They are exposed through the `body` module. [#2468] * Typed headers containing lists that require one or more items now enforce this minimum. [#2482] +### Removed +* `ConnectionInfo::get`. [#2487] + [#2430]: https://github.com/actix/actix-web/pull/2430 [#2468]: https://github.com/actix/actix-web/pull/2468 [#2480]: https://github.com/actix/actix-web/pull/2480 [#2482]: https://github.com/actix/actix-web/pull/2482 [#2484]: https://github.com/actix/actix-web/pull/2484 [#2485]: https://github.com/actix/actix-web/pull/2485 +[#2487]: https://github.com/actix/actix-web/pull/2487 [#2491]: https://github.com/actix/actix-web/pull/2491 [#2492]: https://github.com/actix/actix-web/pull/2492 [#2493]: https://github.com/actix/actix-web/pull/2493 diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index f435784d8..3e62ac2d1 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -16,6 +16,8 @@ * `impl Display` for `header::Quality`. [#2486] * Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491] * `Request::take_conn_data()`. [#2491] +* `Request::take_req_data()`. [#2487] +* `impl Clone` for `RequestHead`. [#2487] ### Changed * Rename `body::BoxBody::{from_body => new}`. [#2468] @@ -40,6 +42,7 @@ [#2468]: https://github.com/actix/actix-web/pull/2468 [#1920]: https://github.com/actix/actix-web/pull/1920 [#2486]: https://github.com/actix/actix-web/pull/2486 +[#2487]: https://github.com/actix/actix-web/pull/2487 [#2488]: https://github.com/actix/actix-web/pull/2488 [#2491]: https://github.com/actix/actix-web/pull/2491 diff --git a/actix-http/examples/hello-world.rs b/actix-http/examples/hello-world.rs index 0a46a89f9..a29903cc4 100644 --- a/actix-http/examples/hello-world.rs +++ b/actix-http/examples/hello-world.rs @@ -1,8 +1,9 @@ use std::{convert::Infallible, io}; -use actix_http::{HttpService, Response, StatusCode}; +use actix_http::{ + header::HeaderValue, HttpMessage, HttpService, Request, Response, StatusCode, +}; use actix_server::Server; -use http::header::HeaderValue; #[actix_rt::main] async fn main() -> io::Result<()> { @@ -13,12 +14,21 @@ async fn main() -> io::Result<()> { HttpService::build() .client_timeout(1000) .client_disconnect(1000) - .finish(|req| async move { + .on_connect_ext(|_, ext| { + ext.insert(42u32); + }) + .finish(|req: Request| async move { log::info!("{:?}", req); let mut res = Response::build(StatusCode::OK); res.insert_header(("x-head", HeaderValue::from_static("dummy value!"))); + let forty_two = req.extensions().get::().unwrap().to_string(); + res.insert_header(( + "x-forty-two", + HeaderValue::from_str(&forty_two).unwrap(), + )); + Ok::<_, Infallible>(res.body("Hello world!")) }) .tcp() diff --git a/actix-http/src/extensions.rs b/actix-http/src/extensions.rs index 164919d87..60b769d13 100644 --- a/actix-http/src/extensions.rs +++ b/actix-http/src/extensions.rs @@ -19,7 +19,7 @@ impl Extensions { #[inline] pub fn new() -> Extensions { Extensions { - map: AHashMap::default(), + map: AHashMap::new(), } } diff --git a/actix-http/src/message.rs b/actix-http/src/message.rs index c8e1ce6db..31c2db718 100644 --- a/actix-http/src/message.rs +++ b/actix-http/src/message.rs @@ -44,13 +44,12 @@ pub trait Head: Default + 'static { F: FnOnce(&MessagePool) -> R; } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct RequestHead { pub method: Method, pub uri: Uri, pub version: Version, pub headers: HeaderMap, - pub extensions: RefCell, pub peer_addr: Option, flags: Flags, } @@ -62,7 +61,6 @@ impl Default for RequestHead { uri: Uri::default(), version: Version::HTTP_11, headers: HeaderMap::with_capacity(16), - extensions: RefCell::new(Extensions::new()), peer_addr: None, flags: Flags::empty(), } @@ -73,7 +71,6 @@ impl Head for RequestHead { fn clear(&mut self) { self.flags = Flags::empty(); self.headers.clear(); - self.extensions.get_mut().clear(); } fn with_pool(f: F) -> R @@ -85,18 +82,6 @@ impl Head for RequestHead { } impl RequestHead { - /// 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() - } - /// Read the message headers. pub fn headers(&self) -> &HeaderMap { &self.headers diff --git a/actix-http/src/request.rs b/actix-http/src/request.rs index 78c0527b5..c7752d470 100644 --- a/actix-http/src/request.rs +++ b/actix-http/src/request.rs @@ -1,8 +1,8 @@ //! HTTP requests. use std::{ - cell::{Ref, RefMut}, - fmt, net, + cell::{Ref, RefCell, RefMut}, + fmt, mem, net, rc::Rc, str, }; @@ -22,6 +22,7 @@ pub struct Request

{ pub(crate) payload: Payload

, pub(crate) head: Message, pub(crate) conn_data: Option>, + pub(crate) req_data: RefCell, } impl

HttpMessage for Request

{ @@ -33,19 +34,19 @@ impl

HttpMessage for Request

{ } fn take_payload(&mut self) -> Payload

{ - std::mem::replace(&mut self.payload, Payload::None) + mem::replace(&mut self.payload, Payload::None) } /// Request extensions #[inline] fn extensions(&self) -> Ref<'_, Extensions> { - self.head.extensions() + self.req_data.borrow() } /// Mutable reference to a the request's extensions #[inline] fn extensions_mut(&self) -> RefMut<'_, Extensions> { - self.head.extensions_mut() + self.req_data.borrow_mut() } } @@ -54,6 +55,7 @@ impl From> for Request { Request { head, payload: Payload::None, + req_data: RefCell::new(Extensions::default()), conn_data: None, } } @@ -65,6 +67,7 @@ impl Request { Request { head: Message::new(), payload: Payload::None, + req_data: RefCell::new(Extensions::default()), conn_data: None, } } @@ -76,6 +79,7 @@ impl

Request

{ Request { payload, head: Message::new(), + req_data: RefCell::new(Extensions::default()), conn_data: None, } } @@ -88,6 +92,7 @@ impl

Request

{ Request { payload, head: self.head, + req_data: self.req_data, conn_data: self.conn_data, }, pl, @@ -101,7 +106,7 @@ impl

Request

{ /// Get request's payload pub fn take_payload(&mut self) -> Payload

{ - std::mem::replace(&mut self.payload, Payload::None) + mem::replace(&mut self.payload, Payload::None) } /// Split request into request head and payload @@ -124,7 +129,7 @@ impl

Request

{ /// Mutable reference to the message's headers. pub fn headers_mut(&mut self) -> &mut HeaderMap { - &mut self.head_mut().headers + &mut self.head.headers } /// Request's uri. @@ -136,7 +141,7 @@ impl

Request

{ /// Mutable reference to the request's uri. #[inline] pub fn uri_mut(&mut self) -> &mut Uri { - &mut self.head_mut().uri + &mut self.head.uri } /// Read the Request method. @@ -198,6 +203,11 @@ impl

Request

{ pub fn take_conn_data(&mut self) -> Option> { self.conn_data.take() } + + /// Returns the request data container, leaving an empty one in it's place. + pub fn take_req_data(&mut self) -> Extensions { + mem::take(&mut self.req_data.get_mut()) + } } impl

fmt::Debug for Request

{ diff --git a/src/app_service.rs b/src/app_service.rs index 5dfc3b5ae..cc5100f04 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -198,6 +198,7 @@ where actix_service::forward_ready!(service); fn call(&self, mut req: Request) -> Self::Future { + let req_data = Rc::new(RefCell::new(req.take_req_data())); let conn_data = req.take_conn_data(); let (head, payload) = req.into_parts(); @@ -207,6 +208,7 @@ where inner.path.reset(); inner.head = head; inner.conn_data = conn_data; + inner.req_data = req_data; req } else { HttpRequest::new( @@ -215,6 +217,7 @@ where self.app_state.clone(), self.app_data.clone(), conn_data, + req_data, ) }; self.service.call(ServiceRequest::new(req, payload)) diff --git a/src/info.rs b/src/info.rs index d928a1e63..71194b24d 100644 --- a/src/info.rs +++ b/src/info.rs @@ -1,4 +1,4 @@ -use std::{cell::Ref, convert::Infallible, net::SocketAddr}; +use std::{convert::Infallible, net::SocketAddr}; use actix_utils::future::{err, ok, Ready}; use derive_more::{Display, Error}; @@ -72,15 +72,7 @@ pub struct ConnectionInfo { } impl ConnectionInfo { - /// Create *ConnectionInfo* instance for a request. - pub fn get<'a>(req: &'a RequestHead, cfg: &AppConfig) -> Ref<'a, Self> { - if !req.extensions().contains::() { - req.extensions_mut().insert(ConnectionInfo::new(req, cfg)); - } - Ref::map(req.extensions(), |e| e.get().unwrap()) - } - - fn new(req: &RequestHead, cfg: &AppConfig) -> ConnectionInfo { + pub(crate) fn new(req: &RequestHead, cfg: &AppConfig) -> ConnectionInfo { let mut host = None; let mut scheme = None; let mut realip_remote_addr = None; diff --git a/src/request.rs b/src/request.rs index d99849eef..d84722d95 100644 --- a/src/request.rs +++ b/src/request.rs @@ -38,6 +38,7 @@ pub(crate) struct HttpRequestInner { pub(crate) path: Path, pub(crate) app_data: SmallVec<[Rc; 4]>, pub(crate) conn_data: Option>, + pub(crate) req_data: Rc>, app_state: Rc, } @@ -49,6 +50,7 @@ impl HttpRequest { app_state: Rc, app_data: Rc, conn_data: Option>, + req_data: Rc>, ) -> HttpRequest { let mut data = SmallVec::<[Rc; 4]>::new(); data.push(app_data); @@ -60,6 +62,7 @@ impl HttpRequest { app_state, app_data: data, conn_data, + req_data, }), } } @@ -156,16 +159,12 @@ impl HttpRequest { self.resource_map().match_name(self.path()) } - /// Request extensions - #[inline] - pub fn extensions(&self) -> Ref<'_, Extensions> { - self.head().extensions() + pub fn req_data(&self) -> Ref<'_, Extensions> { + self.inner.req_data.borrow() } - /// Mutable reference to a the request's extensions - #[inline] - pub fn extensions_mut(&self) -> RefMut<'_, Extensions> { - self.head().extensions_mut() + pub fn req_data_mut(&self) -> RefMut<'_, Extensions> { + self.inner.req_data.borrow_mut() } /// Returns a reference a piece of connection data set in an [on-connect] callback. @@ -248,7 +247,12 @@ impl HttpRequest { /// borrowed. #[inline] pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> { - ConnectionInfo::get(self.head(), self.app_config()) + if !self.extensions().contains::() { + let info = ConnectionInfo::new(self.head(), &*self.app_config()); + self.extensions_mut().insert(info); + } + + Ref::map(self.extensions(), |e| e.get().unwrap()) } /// App config @@ -321,21 +325,18 @@ impl HttpMessage for HttpRequest { type Stream = (); #[inline] - /// Returns Request's headers. fn headers(&self) -> &HeaderMap { &self.head().headers } - /// Request extensions #[inline] fn extensions(&self) -> Ref<'_, Extensions> { - self.inner.head.extensions() + self.req_data() } - /// Mutable reference to a the request's extensions #[inline] fn extensions_mut(&self) -> RefMut<'_, Extensions> { - self.inner.head.extensions_mut() + self.req_data_mut() } #[inline] @@ -348,14 +349,15 @@ impl Drop for HttpRequest { fn drop(&mut self) { // if possible, contribute to current worker's HttpRequest allocation pool - // This relies on no Weak exists anywhere.(There is none) + // This relies on no Weak exists anywhere. (There is none.) if let Some(inner) = Rc::get_mut(&mut self.inner) { if inner.app_state.pool().is_available() { // clear additional app_data and keep the root one for reuse. inner.app_data.truncate(1); - // inner is borrowed mut here. get head's Extension mutably - // to reduce borrow check - inner.head.extensions.get_mut().clear(); + + // Inner is borrowed mut here and; get req data mutably to reduce borrow check. Also + // we know the req_data Rc will not have any cloned at this point to unwrap is okay. + Rc::get_mut(&mut inner.req_data).unwrap().get_mut().clear(); // a re-borrow of pool is necessary here. let req = self.inner.clone(); diff --git a/src/request_data.rs b/src/request_data.rs index 575dc1eb3..680f3e566 100644 --- a/src/request_data.rs +++ b/src/request_data.rs @@ -33,12 +33,11 @@ use crate::{dev::Payload, error::ErrorInternalServerError, Error, FromRequest, H /// req: HttpRequest, /// opt_flag: Option>, /// ) -> impl Responder { -/// // use an optional extractor if the middleware is -/// // not guaranteed to add this type of requests data +/// // use an option extractor if middleware is not guaranteed to add this type of req data /// if let Some(flag) = opt_flag { -/// assert_eq!(&flag.into_inner(), req.extensions().get::().unwrap()); +/// assert_eq!(&flag.into_inner(), req.req_data().get::().unwrap()); /// } -/// +/// /// HttpResponse::Ok() /// } /// ``` @@ -68,7 +67,7 @@ impl FromRequest for ReqData { type Future = Ready>; fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { - if let Some(st) = req.extensions().get::() { + if let Some(st) = req.req_data().get::() { ok(ReqData(st.clone())) } else { log::debug!( diff --git a/src/service.rs b/src/service.rs index d56752f13..88f2ba97a 100644 --- a/src/service.rs +++ b/src/service.rs @@ -194,7 +194,7 @@ impl ServiceRequest { /// Get *ConnectionInfo* for the current request. #[inline] pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> { - ConnectionInfo::get(self.head(), &*self.app_config()) + self.req.connection_info() } /// Get a reference to the Path parameters. diff --git a/src/test.rs b/src/test.rs index bff9c62dc..cfb3ef8f2 100644 --- a/src/test.rs +++ b/src/test.rs @@ -581,7 +581,14 @@ impl TestRequest { let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone()); ServiceRequest::new( - HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data), None), + HttpRequest::new( + self.path, + head, + app_state, + Rc::new(self.app_data), + None, + Default::default(), + ), payload, ) } @@ -599,7 +606,14 @@ impl TestRequest { let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone()); - HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data), None) + HttpRequest::new( + self.path, + head, + app_state, + Rc::new(self.app_data), + None, + Default::default(), + ) } /// Complete request creation and generate `HttpRequest` and `Payload` instances @@ -610,7 +624,14 @@ impl TestRequest { let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone()); - let req = HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data), None); + let req = HttpRequest::new( + self.path, + head, + app_state, + Rc::new(self.app_data), + None, + Default::default(), + ); (req, payload) } From 816d68dee800aafae123802d126c0227a723535f Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 9 Dec 2021 00:46:28 +0000 Subject: [PATCH 10/87] pin h2 temporarily --- actix-http/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 6216af3d1..5c5a0cc86 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -56,7 +56,7 @@ derive_more = "0.99.5" encoding_rs = "0.8" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } futures-util = { version = "0.3.7", default-features = false, features = ["alloc", "sink"] } -h2 = "0.3.1" +h2 = "=0.3.7" http = "0.2.5" httparse = "1.5.1" httpdate = "1.0.1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 836241d46..cdbd0b6aa 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -72,7 +72,7 @@ cfg-if = "1" derive_more = "0.99.5" futures-core = { version = "0.3.7", default-features = false } futures-util = { version = "0.3.7", default-features = false } -h2 = "0.3" +h2 = "=0.3.7" http = "0.2.5" itoa = "0.4" log =" 0.4" From 69fa17f66f8404fda585bf22fc6609a917bf5baa Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 9 Dec 2021 11:27:29 +0000 Subject: [PATCH 11/87] clean future h2 dispatcher --- actix-http/src/h2/dispatcher.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index da2d612f1..8fbefe6de 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -19,13 +19,13 @@ use h2::{ server::{Connection, SendResponse}, Ping, PingPong, }; -use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING}; use log::{error, trace}; use pin_project_lite::pin_project; use crate::{ body::{BodySize, BoxBody, MessageBody}, config::ServiceConfig, + header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING}, service::HttpFlow, Extensions, OnConnectData, Payload, Request, Response, ResponseHead, }; @@ -217,25 +217,28 @@ where return Ok(()); } - // poll response body and send chunks to client. + // poll response body and send chunks to client actix_rt::pin!(body); while let Some(res) = poll_fn(|cx| body.as_mut().poll_next(cx)).await { let mut chunk = res.map_err(|err| DispatchError::ResponseBody(err.into()))?; 'send: loop { + let chunk_size = cmp::min(chunk.len(), CHUNK_SIZE); + // reserve enough space and wait for stream ready. - stream.reserve_capacity(cmp::min(chunk.len(), CHUNK_SIZE)); + stream.reserve_capacity(chunk_size); match poll_fn(|cx| stream.poll_capacity(cx)).await { // No capacity left. drop body and return. None => return Ok(()), - Some(res) => { - // Split chuck to writeable size and send to client. - let cap = res.map_err(DispatchError::SendData)?; + Some(Err(err)) => return Err(DispatchError::SendData(err)), + + Some(Ok(cap)) => { + // split chunk to writeable size and send to client let len = chunk.len(); - let bytes = chunk.split_to(cmp::min(cap, len)); + let bytes = chunk.split_to(cmp::min(len, cap)); stream .send_data(bytes, false) From 774ac7fec435b465f229f44f16fb74c7de971a40 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 9 Dec 2021 13:52:35 +0000 Subject: [PATCH 12/87] provide optimisation path for single-chunk body types (#2497) --- actix-http/CHANGES.md | 2 + actix-http/src/body/boxed.rs | 28 ++++ actix-http/src/body/either.rs | 14 ++ actix-http/src/body/message_body.rs | 230 ++++++++++++++++++++++++++-- actix-http/src/body/none.rs | 10 ++ actix-http/src/encoding/encoder.rs | 72 +++++++-- actix-http/tests/test_openssl.rs | 2 +- 7 files changed, 328 insertions(+), 30 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 3e62ac2d1..7081361e4 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -18,6 +18,7 @@ * `Request::take_conn_data()`. [#2491] * `Request::take_req_data()`. [#2487] * `impl Clone` for `RequestHead`. [#2487] +* New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimisations on body types that are done in exactly one poll/chunk. [#2497] ### Changed * Rename `body::BoxBody::{from_body => new}`. [#2468] @@ -45,6 +46,7 @@ [#2487]: https://github.com/actix/actix-web/pull/2487 [#2488]: https://github.com/actix/actix-web/pull/2488 [#2491]: https://github.com/actix/actix-web/pull/2491 +[#2497]: https://github.com/actix/actix-web/pull/2497 ## 3.0.0-beta.14 - 2021-11-30 diff --git a/actix-http/src/body/boxed.rs b/actix-http/src/body/boxed.rs index c39da10c0..d2469e986 100644 --- a/actix-http/src/body/boxed.rs +++ b/actix-http/src/body/boxed.rs @@ -51,6 +51,34 @@ impl MessageBody for BoxBody { .poll_next(cx) .map_err(|err| Error::new_body().with_cause(err)) } + + fn is_complete_body(&self) -> bool { + self.0.is_complete_body() + } + + fn take_complete_body(&mut self) -> Bytes { + debug_assert!( + self.is_complete_body(), + "boxed type does not allow taking complete body; caller should make sure to \ + call `is_complete_body` first", + ); + + // we do not have DerefMut access to call take_complete_body directly but since + // is_complete_body is true we should expect the entire bytes chunk in one poll_next + + let waker = futures_util::task::noop_waker(); + let mut cx = Context::from_waker(&waker); + + match self.as_pin_mut().poll_next(&mut cx) { + Poll::Ready(Some(Ok(data))) => data, + _ => { + panic!( + "boxed type indicated it allows taking complete body but failed to \ + return Bytes when polled", + ); + } + } + } } #[cfg(test)] diff --git a/actix-http/src/body/either.rs b/actix-http/src/body/either.rs index 6169ee627..6135d834d 100644 --- a/actix-http/src/body/either.rs +++ b/actix-http/src/body/either.rs @@ -67,6 +67,20 @@ where .map_err(|err| Error::new_body().with_cause(err)), } } + + fn is_complete_body(&self) -> bool { + match self { + EitherBody::Left { body } => body.is_complete_body(), + EitherBody::Right { body } => body.is_complete_body(), + } + } + + fn take_complete_body(&mut self) -> Bytes { + match self { + EitherBody::Left { body } => body.take_complete_body(), + EitherBody::Right { body } => body.take_complete_body(), + } + } } #[cfg(test)] diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index 053b6f286..e4020d2af 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -25,10 +25,58 @@ pub trait MessageBody { fn size(&self) -> BodySize; /// Attempt to pull out the next chunk of body bytes. + // TODO: expand documentation fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>>; + + /// Returns true if entire body bytes chunk is obtainable in one call to `poll_next`. + /// + /// This method's implementation should agree with [`take_complete_body`] and should always be + /// checked before taking the body. + /// + /// The default implementation returns `false. + /// + /// [`take_complete_body`]: MessageBody::take_complete_body + fn is_complete_body(&self) -> bool { + false + } + + /// Returns the complete chunk of body bytes. + /// + /// Implementors of this method should note the following: + /// - It is acceptable to skip the omit checks of [`is_complete_body`]. The responsibility of + /// performing this check is delegated to the caller. + /// - If the result of [`is_complete_body`] is conditional, that condition should be given + /// equivalent attention here. + /// - A second call call to [`take_complete_body`] should return an empty `Bytes` or panic. + /// - A call to [`poll_next`] after calling [`take_complete_body`] should return `None` unless + /// the chunk is guaranteed to be empty. + /// + /// The default implementation panics unconditionally, indicating a control flow bug in the + /// calling code. + /// + /// # Panics + /// With a correct implementation, panics if called without first checking [`is_complete_body`]. + /// + /// [`is_complete_body`]: MessageBody::is_complete_body + /// [`take_complete_body`]: MessageBody::take_complete_body + /// [`poll_next`]: MessageBody::poll_next + fn take_complete_body(&mut self) -> Bytes { + assert!( + self.is_complete_body(), + "type ({}) allows taking complete body but did not provide an implementation \ + of `take_complete_body`", + std::any::type_name::() + ); + + unimplemented!( + "type ({}) does not allow taking complete body; caller should make sure to \ + check `is_complete_body` first", + std::any::type_name::() + ); + } } mod foreign_impls { @@ -49,6 +97,14 @@ mod foreign_impls { ) -> Poll>> { match *self {} } + + fn is_complete_body(&self) -> bool { + true + } + + fn take_complete_body(&mut self) -> Bytes { + match *self {} + } } impl MessageBody for () { @@ -66,6 +122,16 @@ mod foreign_impls { ) -> Poll>> { Poll::Ready(None) } + + #[inline] + fn is_complete_body(&self) -> bool { + true + } + + #[inline] + fn take_complete_body(&mut self) -> Bytes { + Bytes::new() + } } impl MessageBody for Box @@ -86,6 +152,16 @@ mod foreign_impls { ) -> Poll>> { Pin::new(self.get_mut().as_mut()).poll_next(cx) } + + #[inline] + fn is_complete_body(&self) -> bool { + self.as_ref().is_complete_body() + } + + #[inline] + fn take_complete_body(&mut self) -> Bytes { + self.as_mut().take_complete_body() + } } impl MessageBody for Pin> @@ -106,6 +182,38 @@ mod foreign_impls { ) -> Poll>> { self.as_mut().poll_next(cx) } + + #[inline] + fn is_complete_body(&self) -> bool { + self.as_ref().is_complete_body() + } + + #[inline] + fn take_complete_body(&mut self) -> Bytes { + debug_assert!( + self.is_complete_body(), + "inner type \"{}\" does not allow taking complete body; caller should make sure to \ + call `is_complete_body` first", + std::any::type_name::(), + ); + + // we do not have DerefMut access to call take_complete_body directly but since + // is_complete_body is true we should expect the entire bytes chunk in one poll_next + + let waker = futures_util::task::noop_waker(); + let mut cx = Context::from_waker(&waker); + + match self.as_mut().poll_next(&mut cx) { + Poll::Ready(Some(Ok(data))) => data, + _ => { + panic!( + "inner type \"{}\" indicated it allows taking complete body but failed to \ + return Bytes when polled", + std::any::type_name::() + ); + } + } + } } impl MessageBody for &'static [u8] { @@ -116,17 +224,23 @@ mod foreign_impls { } fn poll_next( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { - let bytes = mem::take(self.get_mut()); - let bytes = Bytes::from_static(bytes); - Poll::Ready(Some(Ok(bytes))) + Poll::Ready(Some(Ok(self.take_complete_body()))) } } + + fn is_complete_body(&self) -> bool { + true + } + + fn take_complete_body(&mut self) -> Bytes { + Bytes::from_static(mem::take(self)) + } } impl MessageBody for Bytes { @@ -137,16 +251,23 @@ mod foreign_impls { } fn poll_next( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { - let bytes = mem::take(self.get_mut()); - Poll::Ready(Some(Ok(bytes))) + Poll::Ready(Some(Ok(self.take_complete_body()))) } } + + fn is_complete_body(&self) -> bool { + true + } + + fn take_complete_body(&mut self) -> Bytes { + mem::take(self) + } } impl MessageBody for BytesMut { @@ -157,16 +278,23 @@ mod foreign_impls { } fn poll_next( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { - let bytes = mem::take(self.get_mut()).freeze(); - Poll::Ready(Some(Ok(bytes))) + Poll::Ready(Some(Ok(self.take_complete_body()))) } } + + fn is_complete_body(&self) -> bool { + true + } + + fn take_complete_body(&mut self) -> Bytes { + mem::take(self).freeze() + } } impl MessageBody for Vec { @@ -177,16 +305,23 @@ mod foreign_impls { } fn poll_next( - self: Pin<&mut Self>, + mut self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { - let bytes = mem::take(self.get_mut()); - Poll::Ready(Some(Ok(Bytes::from(bytes)))) + Poll::Ready(Some(Ok(self.take_complete_body()))) } } + + fn is_complete_body(&self) -> bool { + true + } + + fn take_complete_body(&mut self) -> Bytes { + Bytes::from(mem::take(self)) + } } impl MessageBody for &'static str { @@ -208,6 +343,14 @@ mod foreign_impls { Poll::Ready(Some(Ok(bytes))) } } + + fn is_complete_body(&self) -> bool { + true + } + + fn take_complete_body(&mut self) -> Bytes { + Bytes::from_static(mem::take(self).as_bytes()) + } } impl MessageBody for String { @@ -228,6 +371,14 @@ mod foreign_impls { Poll::Ready(Some(Ok(Bytes::from(string)))) } } + + fn is_complete_body(&self) -> bool { + true + } + + fn take_complete_body(&mut self) -> Bytes { + Bytes::from(mem::take(self)) + } } impl MessageBody for bytestring::ByteString { @@ -244,6 +395,14 @@ mod foreign_impls { let string = mem::take(self.get_mut()); Poll::Ready(Some(Ok(string.into_bytes()))) } + + fn is_complete_body(&self) -> bool { + true + } + + fn take_complete_body(&mut self) -> Bytes { + mem::take(self).into_bytes() + } } } @@ -406,6 +565,51 @@ mod tests { assert_poll_next!(pl, Bytes::from("test")); } + #[test] + fn take_string() { + let mut data = "test".repeat(2); + let data_bytes = Bytes::from(data.clone()); + assert!(data.is_complete_body()); + assert_eq!(data.take_complete_body(), data_bytes); + + let mut big_data = "test".repeat(64 * 1024); + let data_bytes = Bytes::from(big_data.clone()); + assert!(big_data.is_complete_body()); + assert_eq!(big_data.take_complete_body(), data_bytes); + } + + #[test] + fn take_boxed_equivalence() { + let mut data = Bytes::from_static(b"test"); + assert!(data.is_complete_body()); + assert_eq!(data.take_complete_body(), b"test".as_ref()); + + let mut data = Box::new(Bytes::from_static(b"test")); + assert!(data.is_complete_body()); + assert_eq!(data.take_complete_body(), b"test".as_ref()); + + let mut data = Box::pin(Bytes::from_static(b"test")); + assert!(data.is_complete_body()); + assert_eq!(data.take_complete_body(), b"test".as_ref()); + } + + #[test] + fn take_policy() { + let mut data = Bytes::from_static(b"test"); + // first call returns chunk + assert_eq!(data.take_complete_body(), b"test".as_ref()); + // second call returns empty + assert_eq!(data.take_complete_body(), b"".as_ref()); + + let waker = futures_util::task::noop_waker(); + let mut cx = Context::from_waker(&waker); + let mut data = Bytes::from_static(b"test"); + // take returns whole chunk + assert_eq!(data.take_complete_body(), b"test".as_ref()); + // subsequent poll_next returns None + assert_eq!(Pin::new(&mut data).poll_next(&mut cx), Poll::Ready(None)); + } + // down-casting used to be done with a method on MessageBody trait // test is kept to demonstrate equivalence of Any trait #[actix_rt::test] diff --git a/actix-http/src/body/none.rs b/actix-http/src/body/none.rs index 0fc7c8c9f..bb494078f 100644 --- a/actix-http/src/body/none.rs +++ b/actix-http/src/body/none.rs @@ -40,4 +40,14 @@ impl MessageBody for None { ) -> Poll>> { Poll::Ready(Option::None) } + + #[inline] + fn is_complete_body(&self) -> bool { + true + } + + #[inline] + fn take_complete_body(&mut self) -> Bytes { + Bytes::new() + } } diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index 0886221cc..fa294ab0d 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -53,32 +53,32 @@ impl Encoder { } } - pub fn response(encoding: ContentEncoding, head: &mut ResponseHead, body: B) -> Self { + pub fn response(encoding: ContentEncoding, head: &mut ResponseHead, mut body: B) -> Self { let can_encode = !(head.headers().contains_key(&CONTENT_ENCODING) || head.status == StatusCode::SWITCHING_PROTOCOLS || head.status == StatusCode::NO_CONTENT || encoding == ContentEncoding::Identity || encoding == ContentEncoding::Auto); - match body.size() { - // no need to compress an empty body - BodySize::None => return Self::none(), - - // we cannot assume that Sized is not a stream - BodySize::Sized(_) | BodySize::Stream => {} + // no need to compress an empty body + if matches!(body.size(), BodySize::None) { + return Self::none(); } - // TODO potentially some optimisation for single-chunk responses here by trying to read the - // payload eagerly, stopping after 2 polls if the first is a chunk and the second is None + let body = if body.is_complete_body() { + let body = body.take_complete_body(); + EncoderBody::Full { body } + } else { + EncoderBody::Stream { body } + }; if can_encode { // Modify response body only if encoder is set if let Some(enc) = ContentEncoder::encoder(encoding) { update_head(encoding, head); - head.no_chunking(false); return Encoder { - body: EncoderBody::Stream { body }, + body, encoder: Some(enc), fut: None, eof: false, @@ -87,7 +87,7 @@ impl Encoder { } Encoder { - body: EncoderBody::Stream { body }, + body, encoder: None, fut: None, eof: false, @@ -99,6 +99,7 @@ pin_project! { #[project = EncoderBodyProj] enum EncoderBody { None, + Full { body: Bytes }, Stream { #[pin] body: B }, } } @@ -112,6 +113,7 @@ where fn size(&self) -> BodySize { match self { EncoderBody::None => BodySize::None, + EncoderBody::Full { body } => body.size(), EncoderBody::Stream { body } => body.size(), } } @@ -122,12 +124,32 @@ where ) -> Poll>> { match self.project() { EncoderBodyProj::None => Poll::Ready(None), - + EncoderBodyProj::Full { body } => { + Pin::new(body).poll_next(cx).map_err(|err| match err {}) + } EncoderBodyProj::Stream { body } => body .poll_next(cx) .map_err(|err| EncoderError::Body(err.into())), } } + + fn is_complete_body(&self) -> bool { + match self { + EncoderBody::None => true, + EncoderBody::Full { .. } => true, + EncoderBody::Stream { .. } => false, + } + } + + fn take_complete_body(&mut self) -> Bytes { + match self { + EncoderBody::None => Bytes::new(), + EncoderBody::Full { body } => body.take_complete_body(), + EncoderBody::Stream { .. } => { + panic!("EncoderBody::Stream variant cannot be taken") + } + } + } } impl MessageBody for Encoder @@ -137,10 +159,10 @@ where type Error = EncoderError; fn size(&self) -> BodySize { - if self.encoder.is_none() { - self.body.size() - } else { + if self.encoder.is_some() { BodySize::Stream + } else { + self.body.size() } } @@ -211,6 +233,22 @@ where } } } + + fn is_complete_body(&self) -> bool { + if self.encoder.is_some() { + false + } else { + self.body.is_complete_body() + } + } + + fn take_complete_body(&mut self) -> Bytes { + if self.encoder.is_some() { + panic!("compressed body stream cannot be taken") + } else { + self.body.take_complete_body() + } + } } fn update_head(encoding: ContentEncoding, head: &mut ResponseHead) { @@ -218,6 +256,8 @@ fn update_head(encoding: ContentEncoding, head: &mut ResponseHead) { header::CONTENT_ENCODING, HeaderValue::from_static(encoding.as_str()), ); + + head.no_chunking(false); } enum ContentEncoder { diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index 0c373b8b2..1e371473f 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -101,7 +101,7 @@ async fn test_h2_1() -> io::Result<()> { #[actix_rt::test] async fn test_h2_body() -> io::Result<()> { - let data = "HELLOWORLD".to_owned().repeat(64 * 1024); + let data = "HELLOWORLD".to_owned().repeat(64 * 1024); // 640 KiB let mut srv = test_server(move || { HttpService::build() .h2(|mut req: Request<_>| async move { From f9348d71297c62e623aceb77112a9ec256ceaf3d Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Thu, 9 Dec 2021 17:57:27 +0300 Subject: [PATCH 13/87] add ServiceResponse::into_parts (#2499) --- CHANGES.md | 2 ++ src/service.rs | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 365c89af9..b31624bee 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,6 +9,7 @@ * `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] * Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491] * `HttpRequest::{req_data,req_data_mut}`. [#2487] +* `ServiceResponse::into_parts`. [#2499] ### Changed * Rename `Accept::{mime_precedence => ranked}`. [#2480] @@ -37,6 +38,7 @@ [#2491]: https://github.com/actix/actix-web/pull/2491 [#2492]: https://github.com/actix/actix-web/pull/2492 [#2493]: https://github.com/actix/actix-web/pull/2493 +[#2499]: https://github.com/actix/actix-web/pull/2499 ## 4.0.0-beta.13 - 2021-11-30 diff --git a/src/service.rs b/src/service.rs index 88f2ba97a..36b3858e6 100644 --- a/src/service.rs +++ b/src/service.rs @@ -410,6 +410,12 @@ impl ServiceResponse { self.response.headers_mut() } + /// Destructures `ServiceResponse` into request and response components. + #[inline] + pub fn into_parts(self) -> (HttpRequest, HttpResponse) { + (self.request, self.response) + } + /// Extract response body #[inline] pub fn into_body(self) -> B { From f62383a9751ad9446cbe941cb53167c4a79b75d3 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 10 Dec 2021 22:13:12 +0000 Subject: [PATCH 14/87] unpin h2 --- actix-http/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 5c5a0cc86..ef5f84e4d 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -56,7 +56,7 @@ derive_more = "0.99.5" encoding_rs = "0.8" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } futures-util = { version = "0.3.7", default-features = false, features = ["alloc", "sink"] } -h2 = "=0.3.7" +h2 = "0.3.9" http = "0.2.5" httparse = "1.5.1" httpdate = "1.0.1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index cdbd0b6aa..ed1196f62 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -72,7 +72,7 @@ cfg-if = "1" derive_more = "0.99.5" futures-core = { version = "0.3.7", default-features = false } futures-util = { version = "0.3.7", default-features = false } -h2 = "=0.3.7" +h2 = "0.3.9" http = "0.2.5" itoa = "0.4" log =" 0.4" From 65dd5dfa7b87d1597f0937c509d53e4322e37aa5 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 11 Dec 2021 00:21:30 +0000 Subject: [PATCH 15/87] bump script updates referenced crate versions --- scripts/bump | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/scripts/bump b/scripts/bump index 8b6a3c424..73372ff05 100755 --- a/scripts/bump +++ b/scripts/bump @@ -82,8 +82,33 @@ rm -f $README_FILE.bak echo "manifest, changelog, and readme updated" echo echo "check other references:" -rg "$PACKAGE_NAME =" || true -rg "package = \"$PACKAGE_NAME\"" || true +rg --glob='**/Cargo.toml' "\ +${PACKAGE_NAME} ?= ?\"[^\"]+\"\ +|${PACKAGE_NAME} ?=.*version ?= ?\"([^\"]+)\"\ +|package ?= ?\"${PACKAGE_NAME}\".*version ?= ?\"([^\"]+)\"\ +|version ?= ?\"([^\"]+)\".*package ?= ?\"${PACKAGE_NAME}\"" || true + +echo +read -p "Update all references: (y/N) " UPDATE_REFERENCES +UPDATE_REFERENCES="${UPDATE_REFERENCES:-n}" + +if [ "$UPDATE_REFERENCES" = 'y' ] || [ "$UPDATE_REFERENCES" = 'Y' ]; then + + for f in $(fd Cargo.toml); do + sed -i.bak -E \ + "s/^(${PACKAGE_NAME} ?= ?\")[^\"]+(\")$/\1${NEW_VERSION}\2/g" $f + sed -i.bak -E \ + "s/^(${PACKAGE_NAME} ?=.*version ?= ?\")[^\"]+(\".*)$/\1${NEW_VERSION}\2/g" $f + sed -i.bak -E \ + "s/^(.*package ?= ?\"${PACKAGE_NAME}\".*version ?= ?\")[^\"]+(\".*)$/\1${NEW_VERSION}\2/g" $f + sed -i.bak -E \ + "s/^(.*version ?= ?\")[^\"]+(\".*package ?= ?\"${PACKAGE_NAME}\".*)$/\1${NEW_VERSION}\2/g" $f + + # remove backup file + rm -f $f.bak + done + +fi if [ $MACOS ]; then printf "prepare $PACKAGE_NAME release $NEW_VERSION" | pbcopy From d0f4c809cad35c7a5b3e4f23c91d41391e418df1 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 11 Dec 2021 00:22:09 +0000 Subject: [PATCH 16/87] prepare actix-http release 3.0.0-beta.15 --- Cargo.toml | 2 +- actix-files/Cargo.toml | 2 +- actix-http-test/Cargo.toml | 2 +- actix-http/CHANGES.md | 3 +++ actix-http/Cargo.toml | 2 +- actix-http/README.md | 4 ++-- actix-multipart/Cargo.toml | 2 +- actix-test/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- awc/Cargo.toml | 4 ++-- 10 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cee0680a5..cd7fcdfa2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ actix-service = "2.0.0" actix-utils = "3.0.0" actix-tls = { version = "3.0.0-rc.1", default-features = false, optional = true } -actix-http = "3.0.0-beta.14" +actix-http = "3.0.0-beta.15" actix-router = "0.5.0-beta.2" actix-web-codegen = "0.5.0-beta.5" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 6b6d6d245..932d20ec8 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -23,7 +23,7 @@ experimental-io-uring = ["actix-web/experimental-io-uring", "tokio-uring"] [dependencies] actix-web = { version = "4.0.0-beta.11", default-features = false } -actix-http = "3.0.0-beta.14" +actix-http = "3.0.0-beta.15" actix-service = "2" actix-utils = "3" diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index 7a22cbcc1..2e847c1cc 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -52,4 +52,4 @@ tokio = { version = "1.2", features = ["sync"] } [dev-dependencies] actix-web = { version = "4.0.0-beta.11", default-features = false, features = ["cookies"] } -actix-http = "3.0.0-beta.14" +actix-http = "3.0.0-beta.15" diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 7081361e4..fe47902f7 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 3.0.0-beta.15 - 2021-12-11 ### Added * Add timeout for canceling HTTP/2 server side connection handshake. Default to 5 seconds. [#2483] * HTTP/2 handshake timeout can be configured with `ServiceConfig::client_timeout`. [#2483] diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index ef5f84e4d..5d9035f78 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "3.0.0-beta.14" +version = "3.0.0-beta.15" authors = ["Nikolay Kim "] description = "HTTP primitives for the Actix ecosystem" keywords = ["actix", "http", "framework", "async", "futures"] diff --git a/actix-http/README.md b/actix-http/README.md index 92b86d2a3..a2aa41333 100644 --- a/actix-http/README.md +++ b/actix-http/README.md @@ -3,11 +3,11 @@ > HTTP primitives for the Actix ecosystem. [![crates.io](https://img.shields.io/crates/v/actix-http?label=latest)](https://crates.io/crates/actix-http) -[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.0.0-beta.14)](https://docs.rs/actix-http/3.0.0-beta.14) +[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.0.0-beta.15)](https://docs.rs/actix-http/3.0.0-beta.15) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg)
-[![dependency status](https://deps.rs/crate/actix-http/3.0.0-beta.14/status.svg)](https://deps.rs/crate/actix-http/3.0.0-beta.14) +[![dependency status](https://deps.rs/crate/actix-http/3.0.0-beta.15/status.svg)](https://deps.rs/crate/actix-http/3.0.0-beta.15) [![Download](https://img.shields.io/crates/d/actix-http.svg)](https://crates.io/crates/actix-http) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 04a1d75ee..8178e2ffe 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -28,7 +28,7 @@ twoway = "0.2" [dev-dependencies] actix-rt = "2.2" -actix-http = "3.0.0-beta.14" +actix-http = "3.0.0-beta.15" futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] } tokio = { version = "1", features = ["sync"] } tokio-stream = "0.1" diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index dcaa3e9a3..52e91a6fd 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -29,7 +29,7 @@ openssl = ["tls-openssl", "actix-http/openssl", "awc/openssl"] [dependencies] actix-codec = "0.4.1" -actix-http = "3.0.0-beta.14" +actix-http = "3.0.0-beta.15" actix-http-test = "3.0.0-beta.7" actix-service = "2.0.0" actix-utils = "3.0.0" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 28b5b29ea..63f517adc 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -16,7 +16,7 @@ path = "src/lib.rs" [dependencies] actix = { version = "0.12.0", default-features = false } actix-codec = "0.4.1" -actix-http = "3.0.0-beta.14" +actix-http = "3.0.0-beta.15" actix-web = { version = "4.0.0-beta.11", default-features = false } bytes = "1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index ed1196f62..1dd4df455 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -60,7 +60,7 @@ dangerous-h2c = [] [dependencies] actix-codec = "0.4.1" actix-service = "2.0.0" -actix-http = "3.0.0-beta.14" +actix-http = "3.0.0-beta.15" actix-rt = { version = "2.1", default-features = false } actix-tls = { version = "3.0.0-rc.1", features = ["connect", "uri"] } actix-utils = "3.0.0" @@ -94,7 +94,7 @@ trust-dns-resolver = { version = "0.20.0", optional = true } [dev-dependencies] actix-web = { version = "4.0.0-beta.11", features = ["openssl"] } -actix-http = { version = "3.0.0-beta.14", features = ["openssl"] } +actix-http = { version = "3.0.0-beta.15", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.7", features = ["openssl"] } actix-utils = "3.0.0" actix-server = "2.0.0-rc.1" From e1cdabe5cb803af2041e51ff1fdba2e602db5dbc Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 11 Dec 2021 00:28:38 +0000 Subject: [PATCH 17/87] prepare awc release 3.0.0-beta.13 --- Cargo.toml | 2 +- actix-http-test/Cargo.toml | 2 +- actix-test/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- awc/CHANGES.md | 4 ++++ awc/Cargo.toml | 2 +- awc/README.md | 4 ++-- scripts/bump | 2 ++ 8 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cd7fcdfa2..79ee750a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,7 +108,7 @@ url = "2.1" [dev-dependencies] actix-test = { version = "0.1.0-beta.7", features = ["openssl", "rustls"] } -awc = { version = "3.0.0-beta.11", features = ["openssl"] } +awc = { version = "3.0.0-beta.13", features = ["openssl"] } brotli2 = "0.3.2" criterion = { version = "0.3", features = ["html_reports"] } diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index 2e847c1cc..ee0d14ee6 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -35,7 +35,7 @@ actix-tls = "3.0.0-rc.1" actix-utils = "3.0.0" actix-rt = "2.2" actix-server = "2.0.0-rc.1" -awc = { version = "3.0.0-beta.11", default-features = false } +awc = { version = "3.0.0-beta.13", default-features = false } base64 = "0.13" bytes = "1" diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index 52e91a6fd..79eb0689d 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -35,7 +35,7 @@ actix-service = "2.0.0" actix-utils = "3.0.0" actix-web = { version = "4.0.0-beta.11", default-features = false, features = ["cookies"] } actix-rt = "2.1" -awc = { version = "3.0.0-beta.11", default-features = false, features = ["cookies"] } +awc = { version = "3.0.0-beta.13", default-features = false, features = ["cookies"] } futures-core = { version = "0.3.7", default-features = false, features = ["std"] } futures-util = { version = "0.3.7", default-features = false, features = [] } diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 63f517adc..fa3bb8119 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -28,7 +28,7 @@ tokio = { version = "1", features = ["sync"] } [dev-dependencies] actix-rt = "2.2" actix-test = "0.1.0-beta.7" +awc = { version = "3.0.0-beta.13", default-features = false } -awc = { version = "3.0.0-beta.11", default-features = false } env_logger = "0.9" futures-util = { version = "0.3.7", default-features = false } diff --git a/awc/CHANGES.md b/awc/CHANGES.md index ab3362b72..798b2ce6b 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 3.0.0-beta.13 - 2021-12-11 +* No significant changes since `3.0.0-beta.12`. + + ## 3.0.0-beta.12 - 2021-11-30 * Update `actix-tls` to `3.0.0-rc.1`. [#2474] diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 1dd4df455..9a64c6579 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "3.0.0-beta.12" +version = "3.0.0-beta.13" authors = [ "Nikolay Kim ", "fakeshadow <24548779@qq.com>", diff --git a/awc/README.md b/awc/README.md index b0faedc68..f3c5452fc 100644 --- a/awc/README.md +++ b/awc/README.md @@ -3,9 +3,9 @@ > Async HTTP and WebSocket client library. [![crates.io](https://img.shields.io/crates/v/awc?label=latest)](https://crates.io/crates/awc) -[![Documentation](https://docs.rs/awc/badge.svg?version=3.0.0-beta.12)](https://docs.rs/awc/3.0.0-beta.12) +[![Documentation](https://docs.rs/awc/badge.svg?version=3.0.0-beta.13)](https://docs.rs/awc/3.0.0-beta.13) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/awc) -[![Dependency Status](https://deps.rs/crate/awc/3.0.0-beta.12/status.svg)](https://deps.rs/crate/awc/3.0.0-beta.12) +[![Dependency Status](https://deps.rs/crate/awc/3.0.0-beta.13/status.svg)](https://deps.rs/crate/awc/3.0.0-beta.13) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) ## Documentation & Resources diff --git a/scripts/bump b/scripts/bump index 73372ff05..0c360569d 100755 --- a/scripts/bump +++ b/scripts/bump @@ -41,6 +41,8 @@ cat "$CHANGELOG_FILE" | # if word count of changelog chunk is 0 then insert filler changelog chunk if [ "$(wc -w "$CHANGE_CHUNK_FILE" | awk '{ print $1 }')" = "0" ]; then echo "* No significant changes since \`$CURRENT_VERSION\`." >"$CHANGE_CHUNK_FILE" + echo >>"$CHANGE_CHUNK_FILE" + echo >>"$CHANGE_CHUNK_FILE" fi if [ -n "${2-}" ]; then From cc37be9700f7c2d373cfae01dc1289c652743105 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 11 Dec 2021 00:30:12 +0000 Subject: [PATCH 18/87] prepare actix-web release 4.0.0-beta.14 --- CHANGES.md | 3 +++ Cargo.toml | 2 +- README.md | 4 ++-- actix-files/Cargo.toml | 6 +++--- actix-http-test/Cargo.toml | 2 +- actix-http/Cargo.toml | 5 +++-- actix-multipart/Cargo.toml | 2 +- actix-test/Cargo.toml | 4 ++-- actix-web-actors/Cargo.toml | 2 +- actix-web-codegen/Cargo.toml | 4 ++-- awc/Cargo.toml | 6 +++--- 11 files changed, 22 insertions(+), 18 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b31624bee..3e0b12d9e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 4.0.0-beta.14 - 2021-12-11 ### Added * Methods on `AcceptLanguage`: `ranked` and `preference`. [#2480] * `AcceptEncoding` typed header. [#2482] diff --git a/Cargo.toml b/Cargo.toml index 79ee750a3..9394de71d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "4.0.0-beta.13" +version = "4.0.0-beta.14" authors = ["Nikolay Kim "] description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust" keywords = ["actix", "http", "web", "framework", "async"] diff --git a/README.md b/README.md index c363ece9b..4a1671905 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@

[![crates.io](https://img.shields.io/crates/v/actix-web?label=latest)](https://crates.io/crates/actix-web) -[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.0.0-beta.13)](https://docs.rs/actix-web/4.0.0-beta.13) +[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.0.0-beta.14)](https://docs.rs/actix-web/4.0.0-beta.14) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg) -[![Dependency Status](https://deps.rs/crate/actix-web/4.0.0-beta.13/status.svg)](https://deps.rs/crate/actix-web/4.0.0-beta.13) +[![Dependency Status](https://deps.rs/crate/actix-web/4.0.0-beta.14/status.svg)](https://deps.rs/crate/actix-web/4.0.0-beta.14)
[![build status](https://github.com/actix/actix-web/workflows/CI%20%28Linux%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-web/actions) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 932d20ec8..6480c6fd0 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -22,17 +22,17 @@ path = "src/lib.rs" experimental-io-uring = ["actix-web/experimental-io-uring", "tokio-uring"] [dependencies] -actix-web = { version = "4.0.0-beta.11", default-features = false } actix-http = "3.0.0-beta.15" actix-service = "2" actix-utils = "3" +actix-web = { version = "4.0.0-beta.14", default-features = false } askama_escape = "0.10" bitflags = "1" bytes = "1" +derive_more = "0.99.5" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } http-range = "0.1.4" -derive_more = "0.99.5" log = "0.4" mime = "0.3" mime_guess = "2.0.1" @@ -43,5 +43,5 @@ tokio-uring = { version = "0.1", optional = true } [dev-dependencies] actix-rt = "2.2" -actix-web = "4.0.0-beta.11" actix-test = "0.1.0-beta.7" +actix-web = "4.0.0-beta.14" diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index ee0d14ee6..86acae415 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -51,5 +51,5 @@ tls-openssl = { version = "0.10.9", package = "openssl", optional = true } tokio = { version = "1.2", features = ["sync"] } [dev-dependencies] -actix-web = { version = "4.0.0-beta.11", default-features = false, features = ["cookies"] } +actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["cookies"] } actix-http = "3.0.0-beta.15" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 5d9035f78..fff9fcc16 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -81,10 +81,11 @@ flate2 = { version = "1.0.13", optional = true } zstd = { version = "0.9", optional = true } [dev-dependencies] -actix-server = "2.0.0-rc.1" actix-http-test = { version = "3.0.0-beta.7", features = ["openssl"] } +actix-server = "2.0.0-rc.1" actix-tls = { version = "3.0.0-rc.1", features = ["openssl"] } -actix-web = "4.0.0-beta.13" +actix-web = "4.0.0-beta.14" + async-stream = "0.3" criterion = { version = "0.3", features = ["html_reports"] } env_logger = "0.9" diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 8178e2ffe..9704c255f 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -14,8 +14,8 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "4.0.0-beta.11", default-features = false } actix-utils = "3.0.0" +actix-web = { version = "4.0.0-beta.14", default-features = false } bytes = "1" derive_more = "0.99.5" diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index 79eb0689d..70497d033 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -31,10 +31,10 @@ openssl = ["tls-openssl", "actix-http/openssl", "awc/openssl"] actix-codec = "0.4.1" actix-http = "3.0.0-beta.15" actix-http-test = "3.0.0-beta.7" +actix-rt = "2.1" actix-service = "2.0.0" actix-utils = "3.0.0" -actix-web = { version = "4.0.0-beta.11", default-features = false, features = ["cookies"] } -actix-rt = "2.1" +actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["cookies"] } awc = { version = "3.0.0-beta.13", default-features = false, features = ["cookies"] } futures-core = { version = "0.3.7", default-features = false, features = ["std"] } diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index fa3bb8119..bd8ca9661 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" actix = { version = "0.12.0", default-features = false } actix-codec = "0.4.1" actix-http = "3.0.0-beta.15" -actix-web = { version = "4.0.0-beta.11", default-features = false } +actix-web = { version = "4.0.0-beta.14", default-features = false } bytes = "1" bytestring = "1" diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 8497f0b23..e856aaaf9 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -21,11 +21,11 @@ proc-macro2 = "1" actix-router = "0.5.0-beta.2" [dev-dependencies] -actix-rt = "2.2" actix-macros = "0.2.3" +actix-rt = "2.2" actix-test = "0.1.0-beta.7" actix-utils = "3.0.0" -actix-web = "4.0.0-beta.11" +actix-web = "4.0.0-beta.14" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } trybuild = "1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 9a64c6579..eb5ef1e4a 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -93,13 +93,13 @@ tls-rustls = { package = "rustls", version = "0.20.0", optional = true, features trust-dns-resolver = { version = "0.20.0", optional = true } [dev-dependencies] -actix-web = { version = "4.0.0-beta.11", features = ["openssl"] } actix-http = { version = "3.0.0-beta.15", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.7", features = ["openssl"] } -actix-utils = "3.0.0" actix-server = "2.0.0-rc.1" -actix-tls = { version = "3.0.0-rc.1", features = ["openssl", "rustls"] } actix-test = { version = "0.1.0-beta.7", features = ["openssl", "rustls"] } +actix-tls = { version = "3.0.0-rc.1", features = ["openssl", "rustls"] } +actix-utils = "3.0.0" +actix-web = { version = "4.0.0-beta.14", features = ["openssl"] } brotli2 = "0.3.2" env_logger = "0.9" From ed2f5b40b9ed91b3e1c8e571978e34ed9105e8b1 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 11 Dec 2021 00:31:41 +0000 Subject: [PATCH 19/87] prepare actix-files release 0.6.0-beta.10 --- actix-files/CHANGES.md | 4 ++++ actix-files/Cargo.toml | 2 +- actix-files/README.md | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 63d8efc3f..d6b39e28f 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 0.6.0-beta.10 - 2021-12-11 +* No significant changes since `0.6.0-beta.9`. + + ## 0.6.0-beta.9 - 2021-11-22 * Add crate feature `experimental-io-uring`, enabling async file I/O to be utilized. This feature is only available on Linux OSes with recent kernel versions. This feature is semver-exempt. [#2408] * Add `NamedFile::open_async`. [#2408] diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 6480c6fd0..033e01681 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.6.0-beta.9" +version = "0.6.0-beta.10" authors = [ "Nikolay Kim ", "fakeshadow <24548779@qq.com>", diff --git a/actix-files/README.md b/actix-files/README.md index 84e556fa9..d686e255c 100644 --- a/actix-files/README.md +++ b/actix-files/README.md @@ -3,11 +3,11 @@ > Static file serving for Actix Web [![crates.io](https://img.shields.io/crates/v/actix-files?label=latest)](https://crates.io/crates/actix-files) -[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.0-beta.9)](https://docs.rs/actix-files/0.6.0-beta.9) +[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.0-beta.10)](https://docs.rs/actix-files/0.6.0-beta.10) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![License](https://img.shields.io/crates/l/actix-files.svg)
-[![dependency status](https://deps.rs/crate/actix-files/0.6.0-beta.9/status.svg)](https://deps.rs/crate/actix-files/0.6.0-beta.9) +[![dependency status](https://deps.rs/crate/actix-files/0.6.0-beta.10/status.svg)](https://deps.rs/crate/actix-files/0.6.0-beta.10) [![Download](https://img.shields.io/crates/d/actix-files.svg)](https://crates.io/crates/actix-files) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) From 0cd7c1768270dd3c4245ab9dae331e00be1da858 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 11 Dec 2021 00:32:00 +0000 Subject: [PATCH 20/87] prepare actix-http-test release 3.0.0-beta.9 --- actix-http-test/CHANGES.md | 4 ++++ actix-http-test/Cargo.toml | 2 +- actix-http-test/README.md | 4 ++-- actix-http/Cargo.toml | 2 +- actix-test/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/actix-http-test/CHANGES.md b/actix-http-test/CHANGES.md index 6984e5962..156012168 100644 --- a/actix-http-test/CHANGES.md +++ b/actix-http-test/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 3.0.0-beta.9 - 2021-12-11 +* No significant changes since `3.0.0-beta.8`. + + ## 3.0.0-beta.8 - 2021-11-30 * Update `actix-tls` to `3.0.0-rc.1`. [#2474] diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index 86acae415..449fa342e 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "3.0.0-beta.8" +version = "3.0.0-beta.9" authors = ["Nikolay Kim "] description = "Various helpers for Actix applications to use during testing" keywords = ["http", "web", "framework", "async", "futures"] diff --git a/actix-http-test/README.md b/actix-http-test/README.md index c3e99d259..a0a8f1cd8 100644 --- a/actix-http-test/README.md +++ b/actix-http-test/README.md @@ -3,11 +3,11 @@ > Various helpers for Actix applications to use during testing. [![crates.io](https://img.shields.io/crates/v/actix-http-test?label=latest)](https://crates.io/crates/actix-http-test) -[![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.0.0-beta.8)](https://docs.rs/actix-http-test/3.0.0-beta.8) +[![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.0.0-beta.9)](https://docs.rs/actix-http-test/3.0.0-beta.9) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http-test)
-[![Dependency Status](https://deps.rs/crate/actix-http-test/3.0.0-beta.8/status.svg)](https://deps.rs/crate/actix-http-test/3.0.0-beta.8) +[![Dependency Status](https://deps.rs/crate/actix-http-test/3.0.0-beta.9/status.svg)](https://deps.rs/crate/actix-http-test/3.0.0-beta.9) [![Download](https://img.shields.io/crates/d/actix-http-test.svg)](https://crates.io/crates/actix-http-test) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index fff9fcc16..374a55a62 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -81,7 +81,7 @@ flate2 = { version = "1.0.13", optional = true } zstd = { version = "0.9", optional = true } [dev-dependencies] -actix-http-test = { version = "3.0.0-beta.7", features = ["openssl"] } +actix-http-test = { version = "3.0.0-beta.9", features = ["openssl"] } actix-server = "2.0.0-rc.1" actix-tls = { version = "3.0.0-rc.1", features = ["openssl"] } actix-web = "4.0.0-beta.14" diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index 70497d033..367aa7514 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -30,7 +30,7 @@ openssl = ["tls-openssl", "actix-http/openssl", "awc/openssl"] [dependencies] actix-codec = "0.4.1" actix-http = "3.0.0-beta.15" -actix-http-test = "3.0.0-beta.7" +actix-http-test = "3.0.0-beta.9" actix-rt = "2.1" actix-service = "2.0.0" actix-utils = "3.0.0" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index eb5ef1e4a..e1411fcf6 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -94,7 +94,7 @@ trust-dns-resolver = { version = "0.20.0", optional = true } [dev-dependencies] actix-http = { version = "3.0.0-beta.15", features = ["openssl"] } -actix-http-test = { version = "3.0.0-beta.7", features = ["openssl"] } +actix-http-test = { version = "3.0.0-beta.9", features = ["openssl"] } actix-server = "2.0.0-rc.1" actix-test = { version = "0.1.0-beta.7", features = ["openssl", "rustls"] } actix-tls = { version = "3.0.0-rc.1", features = ["openssl", "rustls"] } From 6481a5fb738cdb9b181fdfe0e0cbfa98f9397c56 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 11 Dec 2021 00:32:26 +0000 Subject: [PATCH 21/87] prepare actix-test release 0.1.0-beta.8 --- Cargo.toml | 2 +- actix-files/Cargo.toml | 2 +- actix-test/CHANGES.md | 4 ++++ actix-test/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- actix-web-codegen/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- 7 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9394de71d..05e4c40bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,7 +107,7 @@ time = { version = "0.3", default-features = false, features = ["formatting"] } url = "2.1" [dev-dependencies] -actix-test = { version = "0.1.0-beta.7", features = ["openssl", "rustls"] } +actix-test = { version = "0.1.0-beta.8", features = ["openssl", "rustls"] } awc = { version = "3.0.0-beta.13", features = ["openssl"] } brotli2 = "0.3.2" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 033e01681..edb7cfab4 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -43,5 +43,5 @@ tokio-uring = { version = "0.1", optional = true } [dev-dependencies] actix-rt = "2.2" -actix-test = "0.1.0-beta.7" +actix-test = "0.1.0-beta.8" actix-web = "4.0.0-beta.14" diff --git a/actix-test/CHANGES.md b/actix-test/CHANGES.md index b739011f0..ec7d3e8d1 100644 --- a/actix-test/CHANGES.md +++ b/actix-test/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 0.1.0-beta.8 - 2021-12-11 +* No significant changes since `0.1.0-beta.7`. + + ## 0.1.0-beta.7 - 2021-11-22 * Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408] diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index 367aa7514..71f99f791 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-test" -version = "0.1.0-beta.7" +version = "0.1.0-beta.8" authors = [ "Nikolay Kim ", "Rob Ede ", diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index bd8ca9661..26388fcc3 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -27,7 +27,7 @@ tokio = { version = "1", features = ["sync"] } [dev-dependencies] actix-rt = "2.2" -actix-test = "0.1.0-beta.7" +actix-test = "0.1.0-beta.8" awc = { version = "3.0.0-beta.13", default-features = false } env_logger = "0.9" diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index e856aaaf9..6ba083c0a 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -23,7 +23,7 @@ actix-router = "0.5.0-beta.2" [dev-dependencies] actix-macros = "0.2.3" actix-rt = "2.2" -actix-test = "0.1.0-beta.7" +actix-test = "0.1.0-beta.8" actix-utils = "3.0.0" actix-web = "4.0.0-beta.14" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index e1411fcf6..48ae27df0 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -96,7 +96,7 @@ trust-dns-resolver = { version = "0.20.0", optional = true } actix-http = { version = "3.0.0-beta.15", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.9", features = ["openssl"] } actix-server = "2.0.0-rc.1" -actix-test = { version = "0.1.0-beta.7", features = ["openssl", "rustls"] } +actix-test = { version = "0.1.0-beta.8", features = ["openssl", "rustls"] } actix-tls = { version = "3.0.0-rc.1", features = ["openssl", "rustls"] } actix-utils = "3.0.0" actix-web = { version = "4.0.0-beta.14", features = ["openssl"] } From fc4e9ff96bf2b41984eb7c322f25e9819a4f5f2b Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 11 Dec 2021 00:33:31 +0000 Subject: [PATCH 22/87] prepare actix-web-codegen release 0.5.0-beta.6 --- Cargo.toml | 2 +- actix-web-codegen/CHANGES.md | 4 ++++ actix-web-codegen/Cargo.toml | 2 +- actix-web-codegen/README.md | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 05e4c40bc..96e2dd797 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,7 @@ actix-tls = { version = "3.0.0-rc.1", default-features = false, optional = true actix-http = "3.0.0-beta.15" actix-router = "0.5.0-beta.2" -actix-web-codegen = "0.5.0-beta.5" +actix-web-codegen = "0.5.0-beta.6" ahash = "0.7" bytes = "1" diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index 3811ef030..309274563 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 0.5.0-beta.6 - 2021-12-11 +* No significant changes since `0.5.0-beta.5`. + + ## 0.5.0-beta.5 - 2021-10-20 * Improve error recovery potential when macro input is invalid. [#2410] * Add `#[actix_web::test]` macro for setting up tests with a runtime. [#2409] diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 6ba083c0a..211f19da6 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-codegen" -version = "0.5.0-beta.5" +version = "0.5.0-beta.6" description = "Routing and runtime macros for Actix Web" homepage = "https://actix.rs" repository = "https://github.com/actix/actix-web.git" diff --git a/actix-web-codegen/README.md b/actix-web-codegen/README.md index 2ffd5b31c..f05d3f22c 100644 --- a/actix-web-codegen/README.md +++ b/actix-web-codegen/README.md @@ -3,11 +3,11 @@ > Routing and runtime macros for Actix Web. [![crates.io](https://img.shields.io/crates/v/actix-web-codegen?label=latest)](https://crates.io/crates/actix-web-codegen) -[![Documentation](https://docs.rs/actix-web-codegen/badge.svg?version=0.5.0-beta.5)](https://docs.rs/actix-web-codegen/0.5.0-beta.5) +[![Documentation](https://docs.rs/actix-web-codegen/badge.svg?version=0.5.0-beta.6)](https://docs.rs/actix-web-codegen/0.5.0-beta.6) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![License](https://img.shields.io/crates/l/actix-web-codegen.svg)
-[![dependency status](https://deps.rs/crate/actix-web-codegen/0.5.0-beta.5/status.svg)](https://deps.rs/crate/actix-web-codegen/0.5.0-beta.5) +[![dependency status](https://deps.rs/crate/actix-web-codegen/0.5.0-beta.6/status.svg)](https://deps.rs/crate/actix-web-codegen/0.5.0-beta.6) [![Download](https://img.shields.io/crates/d/actix-web-codegen.svg)](https://crates.io/crates/actix-web-codegen) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) From 60b030ff5367e3fab25d08e816671b77885a9426 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 11 Dec 2021 00:34:23 +0000 Subject: [PATCH 23/87] prepare actix-web-actors release 4.0.0-beta.8 --- actix-web-actors/CHANGES.md | 3 +++ actix-web-actors/Cargo.toml | 2 +- actix-web-actors/README.md | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md index 898098ed8..d3078499c 100644 --- a/actix-web-actors/CHANGES.md +++ b/actix-web-actors/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 4.0.0-beta.8 - 2021-12-11 * Add `ws:WsResponseBuilder` for building WebSocket session response. [#1920] * Deprecate `ws::{start_with_addr, start_with_protocols}`. [#1920] * Minimum supported Rust version (MSRV) is now 1.52. diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 26388fcc3..128d68c15 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-actors" -version = "4.0.0-beta.7" +version = "4.0.0-beta.8" authors = ["Nikolay Kim "] description = "Actix actors support for Actix Web" keywords = ["actix", "http", "web", "framework", "async"] diff --git a/actix-web-actors/README.md b/actix-web-actors/README.md index 2c29dedf2..954f8273b 100644 --- a/actix-web-actors/README.md +++ b/actix-web-actors/README.md @@ -3,11 +3,11 @@ > Actix actors support for Actix Web. [![crates.io](https://img.shields.io/crates/v/actix-web-actors?label=latest)](https://crates.io/crates/actix-web-actors) -[![Documentation](https://docs.rs/actix-web-actors/badge.svg?version=4.0.0-beta.7)](https://docs.rs/actix-web-actors/4.0.0-beta.7) +[![Documentation](https://docs.rs/actix-web-actors/badge.svg?version=4.0.0-beta.8)](https://docs.rs/actix-web-actors/4.0.0-beta.8) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![License](https://img.shields.io/crates/l/actix-web-actors.svg)
-[![dependency status](https://deps.rs/crate/actix-web-actors/4.0.0-beta.7/status.svg)](https://deps.rs/crate/actix-web-actors/4.0.0-beta.7) +[![dependency status](https://deps.rs/crate/actix-web-actors/4.0.0-beta.8/status.svg)](https://deps.rs/crate/actix-web-actors/4.0.0-beta.8) [![Download](https://img.shields.io/crates/d/actix-web-actors.svg)](https://crates.io/crates/actix-web-actors) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) From 5b0a50249b047152f05627b1d2f581a775d7ce8b Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 11 Dec 2021 00:35:26 +0000 Subject: [PATCH 24/87] prepare actix-multipart release 0.4.0-beta.10 --- actix-multipart/CHANGES.md | 4 ++++ actix-multipart/Cargo.toml | 2 +- actix-multipart/README.md | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index d9ded57a4..8d9c1640f 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 0.4.0-beta.10 - 2021-12-11 +* No significant changes since `0.4.0-beta.9`. + + ## 0.4.0-beta.9 - 2021-12-01 * Polling `Field` after dropping `Multipart` now fails immediately instead of hanging forever. [#2463] diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 9704c255f..6fd1211d9 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-multipart" -version = "0.4.0-beta.9" +version = "0.4.0-beta.10" authors = ["Nikolay Kim "] description = "Multipart form support for Actix Web" keywords = ["http", "web", "framework", "async", "futures"] diff --git a/actix-multipart/README.md b/actix-multipart/README.md index 85c78c5f3..647796557 100644 --- a/actix-multipart/README.md +++ b/actix-multipart/README.md @@ -3,11 +3,11 @@ > Multipart form support for Actix Web. [![crates.io](https://img.shields.io/crates/v/actix-multipart?label=latest)](https://crates.io/crates/actix-multipart) -[![Documentation](https://docs.rs/actix-multipart/badge.svg?version=0.4.0-beta.9)](https://docs.rs/actix-multipart/0.4.0-beta.9) +[![Documentation](https://docs.rs/actix-multipart/badge.svg?version=0.4.0-beta.10)](https://docs.rs/actix-multipart/0.4.0-beta.10) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-multipart.svg)
-[![dependency status](https://deps.rs/crate/actix-multipart/0.4.0-beta.9/status.svg)](https://deps.rs/crate/actix-multipart/0.4.0-beta.9) +[![dependency status](https://deps.rs/crate/actix-multipart/0.4.0-beta.10/status.svg)](https://deps.rs/crate/actix-multipart/0.4.0-beta.10) [![Download](https://img.shields.io/crates/d/actix-multipart.svg)](https://crates.io/crates/actix-multipart) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) From b41b346c00260a164731bf484f930d492be61f67 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 11 Dec 2021 16:05:08 +0000 Subject: [PATCH 25/87] inline trivial body methods --- actix-http/src/body/body_stream.rs | 2 ++ actix-http/src/body/either.rs | 7 +++++++ actix-http/src/body/message_body.rs | 24 ++++++++++++++++++++++-- actix-http/src/body/size.rs | 11 ++++++++--- actix-http/src/body/sized_stream.rs | 2 ++ src/error/mod.rs | 1 + 6 files changed, 42 insertions(+), 5 deletions(-) diff --git a/actix-http/src/body/body_stream.rs b/actix-http/src/body/body_stream.rs index 232d01590..cf4f488b2 100644 --- a/actix-http/src/body/body_stream.rs +++ b/actix-http/src/body/body_stream.rs @@ -27,6 +27,7 @@ where S: Stream>, E: Into> + 'static, { + #[inline] pub fn new(stream: S) -> Self { BodyStream { stream } } @@ -39,6 +40,7 @@ where { type Error = E; + #[inline] fn size(&self) -> BodySize { BodySize::Stream } diff --git a/actix-http/src/body/either.rs b/actix-http/src/body/either.rs index 6135d834d..103b39c5d 100644 --- a/actix-http/src/body/either.rs +++ b/actix-http/src/body/either.rs @@ -23,6 +23,7 @@ pin_project! { impl EitherBody { /// Creates new `EitherBody` using left variant and boxed right variant. + #[inline] pub fn new(body: L) -> Self { Self::Left { body } } @@ -30,11 +31,13 @@ impl EitherBody { impl EitherBody { /// Creates new `EitherBody` using left variant. + #[inline] pub fn left(body: L) -> Self { Self::Left { body } } /// Creates new `EitherBody` using right variant. + #[inline] pub fn right(body: R) -> Self { Self::Right { body } } @@ -47,6 +50,7 @@ where { type Error = Error; + #[inline] fn size(&self) -> BodySize { match self { EitherBody::Left { body } => body.size(), @@ -54,6 +58,7 @@ where } } + #[inline] fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -68,6 +73,7 @@ where } } + #[inline] fn is_complete_body(&self) -> bool { match self { EitherBody::Left { body } => body.is_complete_body(), @@ -75,6 +81,7 @@ where } } + #[inline] fn take_complete_body(&mut self) -> Bytes { match self { EitherBody::Left { body } => body.take_complete_body(), diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index e4020d2af..3e6c8d5cb 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -85,12 +85,10 @@ mod foreign_impls { impl MessageBody for Infallible { type Error = Infallible; - #[inline] fn size(&self) -> BodySize { match *self {} } - #[inline] fn poll_next( self: Pin<&mut Self>, _cx: &mut Context<'_>, @@ -219,6 +217,7 @@ mod foreign_impls { impl MessageBody for &'static [u8] { type Error = Infallible; + #[inline] fn size(&self) -> BodySize { BodySize::Sized(self.len() as u64) } @@ -234,10 +233,12 @@ mod foreign_impls { } } + #[inline] fn is_complete_body(&self) -> bool { true } + #[inline] fn take_complete_body(&mut self) -> Bytes { Bytes::from_static(mem::take(self)) } @@ -246,6 +247,7 @@ mod foreign_impls { impl MessageBody for Bytes { type Error = Infallible; + #[inline] fn size(&self) -> BodySize { BodySize::Sized(self.len() as u64) } @@ -261,10 +263,12 @@ mod foreign_impls { } } + #[inline] fn is_complete_body(&self) -> bool { true } + #[inline] fn take_complete_body(&mut self) -> Bytes { mem::take(self) } @@ -273,6 +277,7 @@ mod foreign_impls { impl MessageBody for BytesMut { type Error = Infallible; + #[inline] fn size(&self) -> BodySize { BodySize::Sized(self.len() as u64) } @@ -288,10 +293,12 @@ mod foreign_impls { } } + #[inline] fn is_complete_body(&self) -> bool { true } + #[inline] fn take_complete_body(&mut self) -> Bytes { mem::take(self).freeze() } @@ -300,6 +307,7 @@ mod foreign_impls { impl MessageBody for Vec { type Error = Infallible; + #[inline] fn size(&self) -> BodySize { BodySize::Sized(self.len() as u64) } @@ -315,10 +323,12 @@ mod foreign_impls { } } + #[inline] fn is_complete_body(&self) -> bool { true } + #[inline] fn take_complete_body(&mut self) -> Bytes { Bytes::from(mem::take(self)) } @@ -327,6 +337,7 @@ mod foreign_impls { impl MessageBody for &'static str { type Error = Infallible; + #[inline] fn size(&self) -> BodySize { BodySize::Sized(self.len() as u64) } @@ -344,10 +355,12 @@ mod foreign_impls { } } + #[inline] fn is_complete_body(&self) -> bool { true } + #[inline] fn take_complete_body(&mut self) -> Bytes { Bytes::from_static(mem::take(self).as_bytes()) } @@ -356,6 +369,7 @@ mod foreign_impls { impl MessageBody for String { type Error = Infallible; + #[inline] fn size(&self) -> BodySize { BodySize::Sized(self.len() as u64) } @@ -372,10 +386,12 @@ mod foreign_impls { } } + #[inline] fn is_complete_body(&self) -> bool { true } + #[inline] fn take_complete_body(&mut self) -> Bytes { Bytes::from(mem::take(self)) } @@ -384,6 +400,7 @@ mod foreign_impls { impl MessageBody for bytestring::ByteString { type Error = Infallible; + #[inline] fn size(&self) -> BodySize { BodySize::Sized(self.len() as u64) } @@ -396,10 +413,12 @@ mod foreign_impls { Poll::Ready(Some(Ok(string.into_bytes()))) } + #[inline] fn is_complete_body(&self) -> bool { true } + #[inline] fn take_complete_body(&mut self) -> Bytes { mem::take(self).into_bytes() } @@ -435,6 +454,7 @@ where { type Error = E; + #[inline] fn size(&self) -> BodySize { self.body.size() } diff --git a/actix-http/src/body/size.rs b/actix-http/src/body/size.rs index d64af9d44..ec7873ca5 100644 --- a/actix-http/src/body/size.rs +++ b/actix-http/src/body/size.rs @@ -1,9 +1,11 @@ /// Body size hint. -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum BodySize { - /// Absence of body can be assumed from method or status code. + /// Implicitly empty body. /// - /// Will skip writing Content-Length header. + /// Will omit the Content-Length header. Used for responses to certain methods (e.g., `HEAD`) or + /// with particular status codes (e.g., 204 No Content). Consumers that read this as a body size + /// hint are allowed to make optimizations that skip reading or writing the payload. None, /// Known size body. @@ -18,6 +20,9 @@ pub enum BodySize { } impl BodySize { + /// Equivalent to `BodySize::Sized(0)`; + pub const ZERO: Self = Self::Sized(0); + /// Returns true if size hint indicates omitted or empty body. /// /// Streams will return false because it cannot be known without reading the stream. diff --git a/actix-http/src/body/sized_stream.rs b/actix-http/src/body/sized_stream.rs index c8606897d..9c1727246 100644 --- a/actix-http/src/body/sized_stream.rs +++ b/actix-http/src/body/sized_stream.rs @@ -27,6 +27,7 @@ where S: Stream>, E: Into> + 'static, { + #[inline] pub fn new(size: u64, stream: S) -> Self { SizedStream { size, stream } } @@ -41,6 +42,7 @@ where { type Error = E; + #[inline] fn size(&self) -> BodySize { BodySize::Sized(self.size as u64) } diff --git a/src/error/mod.rs b/src/error/mod.rs index 4877358a4..64df9f553 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -1,4 +1,5 @@ //! Error and Result module + // This is meant to be a glob import of the whole error module except for `Error`. Rustdoc can't yet // correctly resolve the conflicting `Error` type defined in this module, so these re-exports are // expanded manually. From cea44be67059a5270a892566323e9055809676de Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 11 Dec 2021 16:18:28 +0000 Subject: [PATCH 26/87] add test for returning App from function --- src/app.rs | 21 +++++++++++++++++++++ tests/test_server.rs | 13 +++++++------ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/app.rs b/src/app.rs index ab2081c18..5323cb33a 100644 --- a/src/app.rs +++ b/src/app.rs @@ -706,4 +706,25 @@ mod tests { let body = read_body(resp).await; assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345")); } + + /// compile-only test for returning app type from function + pub fn foreign_app_type() -> App< + impl ServiceFactory< + ServiceRequest, + Response = ServiceResponse, + Config = (), + InitError = (), + Error = Error, + >, + > { + App::new() + // logger can be removed without affecting the return type + .wrap(crate::middleware::Logger::default()) + .route("/", web::to(|| async { "hello" })) + } + + #[test] + fn return_foreign_app_type() { + let _app = foreign_app_type(); + } } diff --git a/tests/test_server.rs b/tests/test_server.rs index 51a78eb28..9b7ef6e1b 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -10,8 +10,13 @@ use std::{ task::{Context, Poll}, }; -use actix_http::header::{ - ContentEncoding, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING, +use actix_web::{ + dev::BodyEncoding, + http::header::{ + ContentEncoding, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING, + }, + middleware::{Compress, NormalizePath, TrailingSlash}, + web, App, Error, HttpResponse, }; use brotli2::write::{BrotliDecoder, BrotliEncoder}; use bytes::Bytes; @@ -31,10 +36,6 @@ use openssl::{ use rand::{distributions::Alphanumeric, Rng}; use zstd::stream::{read::Decoder as ZstdDecoder, write::Encoder as ZstdEncoder}; -use actix_web::dev::BodyEncoding; -use actix_web::middleware::{Compress, NormalizePath, TrailingSlash}; -use actix_web::{web, App, Error, HttpResponse}; - const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ From 551a0d973cf50ff9a22665b0ca7b7f195b997ebf Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 13 Dec 2021 02:58:19 +0000 Subject: [PATCH 27/87] doc tweaks --- actix-http/src/payload.rs | 4 ++-- src/app_service.rs | 1 + src/request.rs | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/actix-http/src/payload.rs b/actix-http/src/payload.rs index 85bfc0b5a..5734af341 100644 --- a/actix-http/src/payload.rs +++ b/actix-http/src/payload.rs @@ -7,10 +7,10 @@ use h2::RecvStream; use crate::error::PayloadError; -/// Type represent boxed payload +/// A boxed payload. pub type PayloadStream = Pin>>>; -/// Type represent streaming payload +/// A streaming payload. pub enum Payload { None, H1(crate::h1::Payload), diff --git a/src/app_service.rs b/src/app_service.rs index cc5100f04..515693db4 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -22,6 +22,7 @@ use crate::{ type Guards = Vec>; /// Service factory to convert `Request` to a `ServiceRequest`. +/// /// It also executes data factories. pub struct AppInit where diff --git a/src/request.rs b/src/request.rs index d84722d95..07fb4eb2d 100644 --- a/src/request.rs +++ b/src/request.rs @@ -349,7 +349,7 @@ impl Drop for HttpRequest { fn drop(&mut self) { // if possible, contribute to current worker's HttpRequest allocation pool - // This relies on no Weak exists anywhere. (There is none.) + // This relies on no weak references to inner existing anywhere within the codebase. if let Some(inner) = Rc::get_mut(&mut self.inner) { if inner.app_state.pool().is_available() { // clear additional app_data and keep the root one for reuse. @@ -360,7 +360,7 @@ impl Drop for HttpRequest { Rc::get_mut(&mut inner.req_data).unwrap().get_mut().clear(); // a re-borrow of pool is necessary here. - let req = self.inner.clone(); + let req = Rc::clone(&self.inner); self.app_state().pool().push(req); } } From 11ee8ec3ab22da345b448c4cf2d8e51781815873 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 13 Dec 2021 16:08:08 +0000 Subject: [PATCH 28/87] align remaining header map terminology (#2510) --- CHANGES.md | 11 + actix-files/src/named.rs | 62 ++--- actix-http/CHANGES.md | 12 +- actix-http/src/h2/service.rs | 15 +- actix-http/src/header/as_name.rs | 5 + actix-http/src/header/into_pair.rs | 56 ++-- actix-http/src/header/into_value.rs | 30 +-- actix-http/src/header/map.rs | 4 +- actix-http/src/header/mod.rs | 6 +- .../src/header/shared/content_encoding.rs | 4 +- actix-http/src/header/shared/http_date.rs | 5 +- actix-http/src/response.rs | 2 +- actix-http/src/response_builder.rs | 20 +- actix-http/src/test.rs | 16 +- awc/CHANGES.md | 5 +- awc/src/builder.rs | 83 +++--- awc/src/client/h1proto.rs | 2 +- awc/src/frozen.rs | 6 +- awc/src/lib.rs | 10 +- awc/src/middleware/redirect.rs | 8 +- awc/src/request.rs | 27 +- awc/src/sender.rs | 4 +- awc/src/test.rs | 21 +- awc/src/ws.rs | 10 +- examples/basic.rs | 4 +- examples/uds.rs | 4 +- src/app.rs | 4 +- src/error/internal.rs | 2 +- src/error/response_error.rs | 2 +- src/http/header/content_disposition.rs | 4 +- src/http/header/content_range.rs | 4 +- src/http/header/entity.rs | 4 +- src/http/header/if_range.rs | 6 +- src/http/header/macros.rs | 8 +- src/http/header/range.rs | 4 +- src/lib.rs | 7 +- src/middleware/default_headers.rs | 127 +++++---- src/middleware/mod.rs | 4 +- src/request_data.rs | 2 +- src/resource.rs | 5 +- src/response/builder.rs | 24 +- src/response/customize_responder.rs | 245 ++++++++++++++++++ src/response/mod.rs | 4 + src/{ => response}/responder.rs | 205 ++------------- src/scope.rs | 2 +- src/test.rs | 14 +- src/web.rs | 2 +- 47 files changed, 608 insertions(+), 503 deletions(-) create mode 100644 src/response/customize_responder.rs rename src/{ => response}/responder.rs (63%) diff --git a/CHANGES.md b/CHANGES.md index 3e0b12d9e..2df820027 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,17 @@ # Changes ## Unreleased - 2021-xx-xx +### Added +* Method on `Responder` trait (`customize`) for customizing responders and `CustomizeResponder` struct. [#2510] +* Implement `Debug` for `DefaultHeaders`. [#2510] + +### Changed +* Align `DefaultHeader` method terminology, deprecating previous methods. [#2510] + +### Removed +* Top-level `EitherExtractError` export. [#2510] + +[#2510]: https://github.com/actix/actix-web/pull/2510 ## 4.0.0-beta.14 - 2021-12-11 diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index 0848543a8..810988f0c 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -2,14 +2,10 @@ use std::{ fmt, fs::Metadata, io, - ops::{Deref, DerefMut}, path::{Path, PathBuf}, time::{SystemTime, UNIX_EPOCH}, }; -#[cfg(unix)] -use std::os::unix::fs::MetadataExt; - use actix_service::{Service, ServiceFactory}; use actix_web::{ body::{self, BoxBody, SizedStream}, @@ -27,6 +23,7 @@ use actix_web::{ Error, HttpMessage, HttpRequest, HttpResponse, Responder, }; use bitflags::bitflags; +use derive_more::{Deref, DerefMut}; use futures_core::future::LocalBoxFuture; use mime_guess::from_path; @@ -71,8 +68,11 @@ impl Default for Flags { /// NamedFile::open_async("./static/index.html").await /// } /// ``` +#[derive(Deref, DerefMut)] pub struct NamedFile { path: PathBuf, + #[deref] + #[deref_mut] file: File, modified: Option, pub(crate) md: Metadata, @@ -364,14 +364,18 @@ impl NamedFile { self } + /// Creates a etag in a format is similar to Apache's. pub(crate) fn etag(&self) -> Option { - // This etag format is similar to Apache's. self.modified.as_ref().map(|mtime| { let ino = { #[cfg(unix)] { + #[cfg(unix)] + use std::os::unix::fs::MetadataExt as _; + self.md.ino() } + #[cfg(not(unix))] { 0 @@ -472,17 +476,17 @@ impl NamedFile { false }; - let mut resp = HttpResponse::build(self.status_code); + let mut res = HttpResponse::build(self.status_code); if self.flags.contains(Flags::PREFER_UTF8) { let ct = equiv_utf8_text(self.content_type.clone()); - resp.insert_header((header::CONTENT_TYPE, ct.to_string())); + res.insert_header((header::CONTENT_TYPE, ct.to_string())); } else { - resp.insert_header((header::CONTENT_TYPE, self.content_type.to_string())); + res.insert_header((header::CONTENT_TYPE, self.content_type.to_string())); } if self.flags.contains(Flags::CONTENT_DISPOSITION) { - resp.insert_header(( + res.insert_header(( header::CONTENT_DISPOSITION, self.content_disposition.to_string(), )); @@ -490,18 +494,18 @@ impl NamedFile { // default compressing if let Some(current_encoding) = self.encoding { - resp.encoding(current_encoding); + res.encoding(current_encoding); } if let Some(lm) = last_modified { - resp.insert_header((header::LAST_MODIFIED, lm.to_string())); + res.insert_header((header::LAST_MODIFIED, lm.to_string())); } if let Some(etag) = etag { - resp.insert_header((header::ETAG, etag.to_string())); + res.insert_header((header::ETAG, etag.to_string())); } - resp.insert_header((header::ACCEPT_RANGES, "bytes")); + res.insert_header((header::ACCEPT_RANGES, "bytes")); let mut length = self.md.len(); let mut offset = 0; @@ -513,24 +517,24 @@ impl NamedFile { length = ranges[0].length; offset = ranges[0].start; - resp.encoding(ContentEncoding::Identity); - resp.insert_header(( + res.encoding(ContentEncoding::Identity); + res.insert_header(( header::CONTENT_RANGE, format!("bytes {}-{}/{}", offset, offset + length - 1, self.md.len()), )); } else { - resp.insert_header((header::CONTENT_RANGE, format!("bytes */{}", length))); - return resp.status(StatusCode::RANGE_NOT_SATISFIABLE).finish(); + res.insert_header((header::CONTENT_RANGE, format!("bytes */{}", length))); + return res.status(StatusCode::RANGE_NOT_SATISFIABLE).finish(); }; } else { - return resp.status(StatusCode::BAD_REQUEST).finish(); + return res.status(StatusCode::BAD_REQUEST).finish(); }; }; if precondition_failed { - return resp.status(StatusCode::PRECONDITION_FAILED).finish(); + return res.status(StatusCode::PRECONDITION_FAILED).finish(); } else if not_modified { - return resp + return res .status(StatusCode::NOT_MODIFIED) .body(body::None::new()) .map_into_boxed_body(); @@ -539,10 +543,10 @@ impl NamedFile { let reader = chunked::new_chunked_read(length, offset, self.file); if offset != 0 || length != self.md.len() { - resp.status(StatusCode::PARTIAL_CONTENT); + res.status(StatusCode::PARTIAL_CONTENT); } - resp.body(SizedStream::new(length, reader)) + res.body(SizedStream::new(length, reader)) } } @@ -586,20 +590,6 @@ fn none_match(etag: Option<&header::EntityTag>, req: &HttpRequest) -> bool { } } -impl Deref for NamedFile { - type Target = File; - - fn deref(&self) -> &Self::Target { - &self.file - } -} - -impl DerefMut for NamedFile { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.file - } -} - impl Responder for NamedFile { type Body = BoxBody; diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index fe47902f7..011e2c608 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,6 +1,12 @@ # Changes ## Unreleased - 2021-xx-xx +### Changed +* Rename trait `IntoHeaderPair => TryIntoHeaderPair`. [#2510] +* Rename `TryIntoHeaderPair::{try_into_header_pair => try_into_pair}`. [#2510] +* Rename trait `IntoHeaderValue => TryIntoHeaderValue`. [#2510] + +[#2510]: https://github.com/actix/actix-web/pull/2510 ## 3.0.0-beta.15 - 2021-12-11 @@ -260,7 +266,7 @@ ## 3.0.0-beta.2 - 2021-02-10 ### Added -* `IntoHeaderPair` trait that allows using typed and untyped headers in the same methods. [#1869] +* `TryIntoHeaderPair` trait that allows using typed and untyped headers in the same methods. [#1869] * `ResponseBuilder::insert_header` method which allows using typed headers. [#1869] * `ResponseBuilder::append_header` method which allows using typed headers. [#1869] * `TestRequest::insert_header` method which allows using typed headers. [#1869] @@ -271,9 +277,9 @@ * `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969] ### Changed -* `ResponseBuilder::content_type` now takes an `impl IntoHeaderValue` to support using typed +* `ResponseBuilder::content_type` now takes an `impl TryIntoHeaderValue` to support using typed `mime` types. [#1894] -* Renamed `IntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std +* Renamed `TryIntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std `TryInto` trait. [#1894] * `Extensions::insert` returns Option of replaced item. [#1904] * Remove `HttpResponseBuilder::json2()`. [#1903] diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index f5821370a..469648054 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -270,10 +270,10 @@ where type Future = H2ServiceHandlerResponse; fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { - self.flow.service.poll_ready(cx).map_err(|e| { - let e = e.into(); - error!("Service readiness error: {:?}", e); - DispatchError::Service(e) + self.flow.service.poll_ready(cx).map_err(|err| { + let err = err.into(); + error!("Service readiness error: {:?}", err); + DispatchError::Service(err) }) } @@ -297,7 +297,6 @@ where T: AsyncRead + AsyncWrite + Unpin, S::Future: 'static, { - Incoming(Dispatcher), Handshake( Option>>, Option, @@ -305,6 +304,7 @@ where OnConnectData, HandshakeWithTimeout, ), + Established(Dispatcher), } pub struct H2ServiceHandlerResponse @@ -332,7 +332,6 @@ where fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.state { - State::Incoming(ref mut disp) => Pin::new(disp).poll(cx), State::Handshake( ref mut srv, ref mut config, @@ -343,7 +342,7 @@ where Ok((conn, timer)) => { let on_connect_data = mem::take(conn_data); - self.state = State::Incoming(Dispatcher::new( + self.state = State::Established(Dispatcher::new( conn, srv.take().unwrap(), config.take().unwrap(), @@ -360,6 +359,8 @@ where Poll::Ready(Err(err)) } }, + + State::Established(ref mut disp) => Pin::new(disp).poll(cx), } } } diff --git a/actix-http/src/header/as_name.rs b/actix-http/src/header/as_name.rs index 17d007f2f..a895010b1 100644 --- a/actix-http/src/header/as_name.rs +++ b/actix-http/src/header/as_name.rs @@ -16,6 +16,7 @@ pub trait Sealed { } impl Sealed for HeaderName { + #[inline] fn try_as_name(&self, _: Seal) -> Result, InvalidHeaderName> { Ok(Cow::Borrowed(self)) } @@ -23,6 +24,7 @@ impl Sealed for HeaderName { impl AsHeaderName for HeaderName {} impl Sealed for &HeaderName { + #[inline] fn try_as_name(&self, _: Seal) -> Result, InvalidHeaderName> { Ok(Cow::Borrowed(*self)) } @@ -30,6 +32,7 @@ impl Sealed for &HeaderName { impl AsHeaderName for &HeaderName {} impl Sealed for &str { + #[inline] fn try_as_name(&self, _: Seal) -> Result, InvalidHeaderName> { HeaderName::from_str(self).map(Cow::Owned) } @@ -37,6 +40,7 @@ impl Sealed for &str { impl AsHeaderName for &str {} impl Sealed for String { + #[inline] fn try_as_name(&self, _: Seal) -> Result, InvalidHeaderName> { HeaderName::from_str(self).map(Cow::Owned) } @@ -44,6 +48,7 @@ impl Sealed for String { impl AsHeaderName for String {} impl Sealed for &String { + #[inline] fn try_as_name(&self, _: Seal) -> Result, InvalidHeaderName> { HeaderName::from_str(self).map(Cow::Owned) } diff --git a/actix-http/src/header/into_pair.rs b/actix-http/src/header/into_pair.rs index b4250e06e..91c3e6640 100644 --- a/actix-http/src/header/into_pair.rs +++ b/actix-http/src/header/into_pair.rs @@ -1,22 +1,20 @@ -//! [`IntoHeaderPair`] trait and implementations. +//! [`TryIntoHeaderPair`] trait and implementations. use std::convert::TryFrom as _; -use http::{ - header::{HeaderName, InvalidHeaderName, InvalidHeaderValue}, - Error as HttpError, HeaderValue, +use super::{ + Header, HeaderName, HeaderValue, InvalidHeaderName, InvalidHeaderValue, TryIntoHeaderValue, }; +use crate::error::HttpError; -use super::{Header, IntoHeaderValue}; - -/// An interface for types that can be converted into a [`HeaderName`]/[`HeaderValue`] pair for +/// An interface for types that can be converted into a [`HeaderName`] + [`HeaderValue`] pair for /// insertion into a [`HeaderMap`]. /// /// [`HeaderMap`]: super::HeaderMap -pub trait IntoHeaderPair: Sized { +pub trait TryIntoHeaderPair: Sized { type Error: Into; - fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error>; + fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error>; } #[derive(Debug)] @@ -34,14 +32,14 @@ impl From for HttpError { } } -impl IntoHeaderPair for (HeaderName, V) +impl TryIntoHeaderPair for (HeaderName, V) where - V: IntoHeaderValue, + V: TryIntoHeaderValue, V::Error: Into, { type Error = InvalidHeaderPart; - fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> { + fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> { let (name, value) = self; let value = value .try_into_value() @@ -50,14 +48,14 @@ where } } -impl IntoHeaderPair for (&HeaderName, V) +impl TryIntoHeaderPair for (&HeaderName, V) where - V: IntoHeaderValue, + V: TryIntoHeaderValue, V::Error: Into, { type Error = InvalidHeaderPart; - fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> { + fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> { let (name, value) = self; let value = value .try_into_value() @@ -66,14 +64,14 @@ where } } -impl IntoHeaderPair for (&[u8], V) +impl TryIntoHeaderPair for (&[u8], V) where - V: IntoHeaderValue, + V: TryIntoHeaderValue, V::Error: Into, { type Error = InvalidHeaderPart; - fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> { + fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> { let (name, value) = self; let name = HeaderName::try_from(name).map_err(InvalidHeaderPart::Name)?; let value = value @@ -83,14 +81,14 @@ where } } -impl IntoHeaderPair for (&str, V) +impl TryIntoHeaderPair for (&str, V) where - V: IntoHeaderValue, + V: TryIntoHeaderValue, V::Error: Into, { type Error = InvalidHeaderPart; - fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> { + fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> { let (name, value) = self; let name = HeaderName::try_from(name).map_err(InvalidHeaderPart::Name)?; let value = value @@ -100,23 +98,25 @@ where } } -impl IntoHeaderPair for (String, V) +impl TryIntoHeaderPair for (String, V) where - V: IntoHeaderValue, + V: TryIntoHeaderValue, V::Error: Into, { type Error = InvalidHeaderPart; - fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> { + #[inline] + fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> { let (name, value) = self; - (name.as_str(), value).try_into_header_pair() + (name.as_str(), value).try_into_pair() } } -impl IntoHeaderPair for T { - type Error = ::Error; +impl TryIntoHeaderPair for T { + type Error = ::Error; - fn try_into_header_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> { + #[inline] + fn try_into_pair(self) -> Result<(HeaderName, HeaderValue), Self::Error> { Ok((T::name(), self.try_into_value()?)) } } diff --git a/actix-http/src/header/into_value.rs b/actix-http/src/header/into_value.rs index bad05db64..6d369ee65 100644 --- a/actix-http/src/header/into_value.rs +++ b/actix-http/src/header/into_value.rs @@ -1,4 +1,4 @@ -//! [`IntoHeaderValue`] trait and implementations. +//! [`TryIntoHeaderValue`] trait and implementations. use std::convert::TryFrom as _; @@ -7,7 +7,7 @@ use http::{header::InvalidHeaderValue, Error as HttpError, HeaderValue}; use mime::Mime; /// An interface for types that can be converted into a [`HeaderValue`]. -pub trait IntoHeaderValue: Sized { +pub trait TryIntoHeaderValue: Sized { /// The type returned in the event of a conversion error. type Error: Into; @@ -15,7 +15,7 @@ pub trait IntoHeaderValue: Sized { fn try_into_value(self) -> Result; } -impl IntoHeaderValue for HeaderValue { +impl TryIntoHeaderValue for HeaderValue { type Error = InvalidHeaderValue; #[inline] @@ -24,7 +24,7 @@ impl IntoHeaderValue for HeaderValue { } } -impl IntoHeaderValue for &HeaderValue { +impl TryIntoHeaderValue for &HeaderValue { type Error = InvalidHeaderValue; #[inline] @@ -33,7 +33,7 @@ impl IntoHeaderValue for &HeaderValue { } } -impl IntoHeaderValue for &str { +impl TryIntoHeaderValue for &str { type Error = InvalidHeaderValue; #[inline] @@ -42,7 +42,7 @@ impl IntoHeaderValue for &str { } } -impl IntoHeaderValue for &[u8] { +impl TryIntoHeaderValue for &[u8] { type Error = InvalidHeaderValue; #[inline] @@ -51,7 +51,7 @@ impl IntoHeaderValue for &[u8] { } } -impl IntoHeaderValue for Bytes { +impl TryIntoHeaderValue for Bytes { type Error = InvalidHeaderValue; #[inline] @@ -60,7 +60,7 @@ impl IntoHeaderValue for Bytes { } } -impl IntoHeaderValue for Vec { +impl TryIntoHeaderValue for Vec { type Error = InvalidHeaderValue; #[inline] @@ -69,7 +69,7 @@ impl IntoHeaderValue for Vec { } } -impl IntoHeaderValue for String { +impl TryIntoHeaderValue for String { type Error = InvalidHeaderValue; #[inline] @@ -78,7 +78,7 @@ impl IntoHeaderValue for String { } } -impl IntoHeaderValue for usize { +impl TryIntoHeaderValue for usize { type Error = InvalidHeaderValue; #[inline] @@ -87,7 +87,7 @@ impl IntoHeaderValue for usize { } } -impl IntoHeaderValue for i64 { +impl TryIntoHeaderValue for i64 { type Error = InvalidHeaderValue; #[inline] @@ -96,7 +96,7 @@ impl IntoHeaderValue for i64 { } } -impl IntoHeaderValue for u64 { +impl TryIntoHeaderValue for u64 { type Error = InvalidHeaderValue; #[inline] @@ -105,7 +105,7 @@ impl IntoHeaderValue for u64 { } } -impl IntoHeaderValue for i32 { +impl TryIntoHeaderValue for i32 { type Error = InvalidHeaderValue; #[inline] @@ -114,7 +114,7 @@ impl IntoHeaderValue for i32 { } } -impl IntoHeaderValue for u32 { +impl TryIntoHeaderValue for u32 { type Error = InvalidHeaderValue; #[inline] @@ -123,7 +123,7 @@ impl IntoHeaderValue for u32 { } } -impl IntoHeaderValue for Mime { +impl TryIntoHeaderValue for Mime { type Error = InvalidHeaderValue; #[inline] diff --git a/actix-http/src/header/map.rs b/actix-http/src/header/map.rs index 12c8f9462..748410375 100644 --- a/actix-http/src/header/map.rs +++ b/actix-http/src/header/map.rs @@ -333,7 +333,7 @@ impl HeaderMap { } } - /// Inserts a name-value pair into the map. + /// Inserts (overrides) a name-value pair in the map. /// /// If the map already contained this key, the new value is associated with the key and all /// previous values are removed and returned as a `Removed` iterator. The key is not updated; @@ -372,7 +372,7 @@ impl HeaderMap { Removed::new(value) } - /// Inserts a name-value pair into the map. + /// Appends a name-value pair to the map. /// /// If the map already contained this key, the new value is added to the list of values /// currently associated with the key. The key is not updated; this matters for types that can diff --git a/actix-http/src/header/mod.rs b/actix-http/src/header/mod.rs index 5fe76381b..dd4f06106 100644 --- a/actix-http/src/header/mod.rs +++ b/actix-http/src/header/mod.rs @@ -37,8 +37,8 @@ mod shared; mod utils; pub use self::as_name::AsHeaderName; -pub use self::into_pair::IntoHeaderPair; -pub use self::into_value::IntoHeaderValue; +pub use self::into_pair::TryIntoHeaderPair; +pub use self::into_value::TryIntoHeaderValue; pub use self::map::HeaderMap; pub use self::shared::{ parse_extended_value, q, Charset, ContentEncoding, ExtendedValue, HttpDate, LanguageTag, @@ -49,7 +49,7 @@ pub use self::utils::{ }; /// An interface for types that already represent a valid header. -pub trait Header: IntoHeaderValue { +pub trait Header: TryIntoHeaderValue { /// Returns the name of the header field fn name() -> HeaderName; diff --git a/actix-http/src/header/shared/content_encoding.rs b/actix-http/src/header/shared/content_encoding.rs index 073d90dce..a6e52138d 100644 --- a/actix-http/src/header/shared/content_encoding.rs +++ b/actix-http/src/header/shared/content_encoding.rs @@ -5,7 +5,7 @@ use http::header::InvalidHeaderValue; use crate::{ error::ParseError, - header::{self, from_one_raw_str, Header, HeaderName, HeaderValue, IntoHeaderValue}, + header::{self, from_one_raw_str, Header, HeaderName, HeaderValue, TryIntoHeaderValue}, HttpMessage, }; @@ -96,7 +96,7 @@ impl TryFrom<&str> for ContentEncoding { } } -impl IntoHeaderValue for ContentEncoding { +impl TryIntoHeaderValue for ContentEncoding { type Error = InvalidHeaderValue; fn try_into_value(self) -> Result { diff --git a/actix-http/src/header/shared/http_date.rs b/actix-http/src/header/shared/http_date.rs index 228f6f00e..473d6cad0 100644 --- a/actix-http/src/header/shared/http_date.rs +++ b/actix-http/src/header/shared/http_date.rs @@ -4,7 +4,8 @@ use bytes::BytesMut; use http::header::{HeaderValue, InvalidHeaderValue}; use crate::{ - config::DATE_VALUE_LENGTH, error::ParseError, header::IntoHeaderValue, helpers::MutWriter, + config::DATE_VALUE_LENGTH, error::ParseError, header::TryIntoHeaderValue, + helpers::MutWriter, }; /// A timestamp with HTTP-style formatting and parsing. @@ -29,7 +30,7 @@ impl fmt::Display for HttpDate { } } -impl IntoHeaderValue for HttpDate { +impl TryIntoHeaderValue for HttpDate { type Error = InvalidHeaderValue; fn try_into_value(self) -> Result { diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index 861cab2cb..9f799f669 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -11,7 +11,7 @@ use bytestring::ByteString; use crate::{ body::{BoxBody, MessageBody}, extensions::Extensions, - header::{self, HeaderMap, IntoHeaderValue}, + header::{self, HeaderMap, TryIntoHeaderValue}, message::{BoxedResponseHead, ResponseHead}, Error, ResponseBuilder, StatusCode, }; diff --git a/actix-http/src/response_builder.rs b/actix-http/src/response_builder.rs index dfc2612fb..adbe86fca 100644 --- a/actix-http/src/response_builder.rs +++ b/actix-http/src/response_builder.rs @@ -8,7 +8,7 @@ use std::{ use crate::{ body::{EitherBody, MessageBody}, error::{Error, HttpError}, - header::{self, IntoHeaderPair, IntoHeaderValue}, + header::{self, TryIntoHeaderPair, TryIntoHeaderValue}, message::{BoxedResponseHead, ConnectionType, ResponseHead}, Extensions, Response, StatusCode, }; @@ -90,12 +90,9 @@ impl ResponseBuilder { /// assert!(res.headers().contains_key("content-type")); /// assert!(res.headers().contains_key("x-test")); /// ``` - pub fn insert_header(&mut self, header: H) -> &mut Self - where - H: IntoHeaderPair, - { + pub fn insert_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self { if let Some(parts) = self.inner() { - match header.try_into_header_pair() { + match header.try_into_pair() { Ok((key, value)) => { parts.headers.insert(key, value); } @@ -121,12 +118,9 @@ impl ResponseBuilder { /// assert_eq!(res.headers().get_all("content-type").count(), 1); /// assert_eq!(res.headers().get_all("x-test").count(), 2); /// ``` - pub fn append_header(&mut self, header: H) -> &mut Self - where - H: IntoHeaderPair, - { + pub fn append_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self { if let Some(parts) = self.inner() { - match header.try_into_header_pair() { + match header.try_into_pair() { Ok((key, value)) => parts.headers.append(key, value), Err(e) => self.err = Some(e.into()), }; @@ -157,7 +151,7 @@ impl ResponseBuilder { #[inline] pub fn upgrade(&mut self, value: V) -> &mut Self where - V: IntoHeaderValue, + V: TryIntoHeaderValue, { if let Some(parts) = self.inner() { parts.set_connection_type(ConnectionType::Upgrade); @@ -195,7 +189,7 @@ impl ResponseBuilder { #[inline] pub fn content_type(&mut self, value: V) -> &mut Self where - V: IntoHeaderValue, + V: TryIntoHeaderValue, { if let Some(parts) = self.inner() { match value.try_into_value() { diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs index ec781743d..7e26ee865 100644 --- a/actix-http/src/test.rs +++ b/actix-http/src/test.rs @@ -14,7 +14,7 @@ use bytes::{Bytes, BytesMut}; use http::{Method, Uri, Version}; use crate::{ - header::{HeaderMap, IntoHeaderPair}, + header::{HeaderMap, TryIntoHeaderPair}, payload::Payload, Request, }; @@ -92,11 +92,8 @@ impl TestRequest { } /// Insert a header, replacing any that were set with an equivalent field name. - pub fn insert_header(&mut self, header: H) -> &mut Self - where - H: IntoHeaderPair, - { - match header.try_into_header_pair() { + pub fn insert_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self { + match header.try_into_pair() { Ok((key, value)) => { parts(&mut self.0).headers.insert(key, value); } @@ -109,11 +106,8 @@ impl TestRequest { } /// Append a header, keeping any that were set with an equivalent field name. - pub fn append_header(&mut self, header: H) -> &mut Self - where - H: IntoHeaderPair, - { - match header.try_into_header_pair() { + pub fn append_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self { + match header.try_into_pair() { Ok((key, value)) => { parts(&mut self.0).headers.append(key, value); } diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 798b2ce6b..8a3fea46a 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx +* Add `ClientBuilder::add_default_header` and deprecate `ClientBuilder::header`. [#2510] + +[#2510]: https://github.com/actix/actix-web/pull/2510 ## 3.0.0-beta.13 - 2021-12-11 @@ -60,7 +63,7 @@ * `ConnectorService` type is renamed to `BoxConnectorService`. [#2081] * Fix http/https encoding when enabling `compress` feature. [#2116] * Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header - methods now take `IntoHeaderPair` tuples. [#2094] + methods now take `TryIntoHeaderPair` tuples. [#2094] [#2081]: https://github.com/actix/actix-web/pull/2081 [#2094]: https://github.com/actix/actix-web/pull/2094 diff --git a/awc/src/builder.rs b/awc/src/builder.rs index 43e5c0def..30f203bb8 100644 --- a/awc/src/builder.rs +++ b/awc/src/builder.rs @@ -2,7 +2,7 @@ use std::{convert::TryFrom, fmt, net::IpAddr, rc::Rc, time::Duration}; use actix_http::{ error::HttpError, - header::{self, HeaderMap, HeaderName}, + header::{self, HeaderMap, HeaderName, TryIntoHeaderPair}, Uri, }; use actix_rt::net::{ActixStream, TcpStream}; @@ -21,11 +21,11 @@ use crate::{ /// This type can be used to construct an instance of `Client` through a /// builder-like pattern. pub struct ClientBuilder { - default_headers: bool, max_http_version: Option, stream_window_size: Option, conn_window_size: Option, - headers: HeaderMap, + fundamental_headers: bool, + default_headers: HeaderMap, timeout: Option, connector: Connector, middleware: M, @@ -44,15 +44,15 @@ impl ClientBuilder { (), > { ClientBuilder { - middleware: (), - default_headers: true, - headers: HeaderMap::new(), - timeout: Some(Duration::from_secs(5)), - local_address: None, - connector: Connector::new(), max_http_version: None, stream_window_size: None, conn_window_size: None, + fundamental_headers: true, + default_headers: HeaderMap::new(), + timeout: Some(Duration::from_secs(5)), + connector: Connector::new(), + middleware: (), + local_address: None, max_redirects: 10, } } @@ -78,8 +78,8 @@ where { ClientBuilder { middleware: self.middleware, + fundamental_headers: self.fundamental_headers, default_headers: self.default_headers, - headers: self.headers, timeout: self.timeout, local_address: self.local_address, connector, @@ -153,30 +153,46 @@ where self } - /// Do not add default request headers. + /// Do not add fundamental default request headers. + /// /// By default `Date` and `User-Agent` headers are set. pub fn no_default_headers(mut self) -> Self { - self.default_headers = false; + self.fundamental_headers = false; self } - /// Add default header. Headers added by this method - /// get added to every request. + /// Add default header. + /// + /// Headers added by this method get added to every request unless overriden by . + /// + /// # Panics + /// Panics if header name or value is invalid. + pub fn add_default_header(mut self, header: impl TryIntoHeaderPair) -> Self { + match header.try_into_pair() { + Ok((key, value)) => self.default_headers.append(key, value), + Err(err) => panic!("Header error: {:?}", err.into()), + } + + self + } + + #[doc(hidden)] + #[deprecated(since = "3.0.0", note = "Prefer `add_default_header((key, value))`.")] pub fn header(mut self, key: K, value: V) -> Self where HeaderName: TryFrom, >::Error: fmt::Debug + Into, - V: header::IntoHeaderValue, + V: header::TryIntoHeaderValue, V::Error: fmt::Debug, { match HeaderName::try_from(key) { Ok(key) => match value.try_into_value() { Ok(value) => { - self.headers.append(key, value); + self.default_headers.append(key, value); } - Err(e) => log::error!("Header value error: {:?}", e), + Err(err) => log::error!("Header value error: {:?}", err), }, - Err(e) => log::error!("Header name error: {:?}", e), + Err(err) => log::error!("Header name error: {:?}", err), } self } @@ -190,10 +206,10 @@ where Some(password) => format!("{}:{}", username, password), None => format!("{}:", username), }; - self.header( + self.add_default_header(( header::AUTHORIZATION, format!("Basic {}", base64::encode(&auth)), - ) + )) } /// Set client wide HTTP bearer authentication header @@ -201,13 +217,12 @@ where where T: fmt::Display, { - self.header(header::AUTHORIZATION, format!("Bearer {}", token)) + self.add_default_header((header::AUTHORIZATION, format!("Bearer {}", token))) } - /// Registers middleware, in the form of a middleware component (type), - /// that runs during inbound and/or outbound processing in the request - /// life-cycle (request -> response), modifying request/response as - /// necessary, across all requests managed by the Client. + /// Registers middleware, in the form of a middleware component (type), that runs during inbound + /// and/or outbound processing in the request life-cycle (request -> response), + /// modifying request/response as necessary, across all requests managed by the `Client`. pub fn wrap( self, mw: M1, @@ -218,11 +233,11 @@ where { ClientBuilder { middleware: NestTransform::new(self.middleware, mw), - default_headers: self.default_headers, + fundamental_headers: self.fundamental_headers, max_http_version: self.max_http_version, stream_window_size: self.stream_window_size, conn_window_size: self.conn_window_size, - headers: self.headers, + default_headers: self.default_headers, timeout: self.timeout, connector: self.connector, local_address: self.local_address, @@ -237,10 +252,10 @@ where M::Transform: Service, { - let redirect_time = self.max_redirects; + let max_redirects = self.max_redirects; - if redirect_time > 0 { - self.wrap(Redirect::new().max_redirect_times(redirect_time)) + if max_redirects > 0 { + self.wrap(Redirect::new().max_redirect_times(max_redirects)) ._finish() } else { self._finish() @@ -272,7 +287,7 @@ where let connector = boxed::rc_service(self.middleware.new_transform(connector)); Client(ClientConfig { - headers: Rc::new(self.headers), + default_headers: Rc::new(self.default_headers), timeout: self.timeout, connector, }) @@ -288,7 +303,7 @@ mod tests { let client = ClientBuilder::new().basic_auth("username", Some("password")); assert_eq!( client - .headers + .default_headers .get(header::AUTHORIZATION) .unwrap() .to_str() @@ -299,7 +314,7 @@ mod tests { let client = ClientBuilder::new().basic_auth("username", None); assert_eq!( client - .headers + .default_headers .get(header::AUTHORIZATION) .unwrap() .to_str() @@ -313,7 +328,7 @@ mod tests { let client = ClientBuilder::new().bearer_auth("someS3cr3tAutht0k3n"); assert_eq!( client - .headers + .default_headers .get(header::AUTHORIZATION) .unwrap() .to_str() diff --git a/awc/src/client/h1proto.rs b/awc/src/client/h1proto.rs index c8b9a3fae..1028a2178 100644 --- a/awc/src/client/h1proto.rs +++ b/awc/src/client/h1proto.rs @@ -9,7 +9,7 @@ use actix_http::{ body::{BodySize, MessageBody}, error::PayloadError, h1, - header::{HeaderMap, IntoHeaderValue, EXPECT, HOST}, + header::{HeaderMap, TryIntoHeaderValue, EXPECT, HOST}, Payload, RequestHeadType, ResponseHead, StatusCode, }; use actix_utils::future::poll_fn; diff --git a/awc/src/frozen.rs b/awc/src/frozen.rs index 7497f85c8..cd93a1d60 100644 --- a/awc/src/frozen.rs +++ b/awc/src/frozen.rs @@ -6,7 +6,7 @@ use serde::Serialize; use actix_http::{ error::HttpError, - header::{HeaderMap, HeaderName, IntoHeaderValue}, + header::{HeaderMap, HeaderName, TryIntoHeaderValue}, Method, RequestHead, Uri, }; @@ -114,7 +114,7 @@ impl FrozenClientRequest { where HeaderName: TryFrom, >::Error: Into, - V: IntoHeaderValue, + V: TryIntoHeaderValue, { self.extra_headers(HeaderMap::new()) .extra_header(key, value) @@ -142,7 +142,7 @@ impl FrozenSendBuilder { where HeaderName: TryFrom, >::Error: Into, - V: IntoHeaderValue, + V: TryIntoHeaderValue, { match HeaderName::try_from(key) { Ok(key) => match value.try_into_value() { diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 06fd33fac..00c559406 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -168,7 +168,7 @@ pub struct Client(ClientConfig); #[derive(Clone)] pub(crate) struct ClientConfig { pub(crate) connector: BoxConnectorService, - pub(crate) headers: Rc, + pub(crate) default_headers: Rc, pub(crate) timeout: Option, } @@ -204,7 +204,9 @@ impl Client { { let mut req = ClientRequest::new(method, url, self.0.clone()); - for header in self.0.headers.iter() { + for header in self.0.default_headers.iter() { + // header map is empty + // TODO: probably append instead req = req.insert_header_if_none(header); } req @@ -297,7 +299,7 @@ impl Client { >::Error: Into, { let mut req = ws::WebsocketsRequest::new(url, self.0.clone()); - for (key, value) in self.0.headers.iter() { + for (key, value) in self.0.default_headers.iter() { req.head.headers.insert(key.clone(), value.clone()); } req @@ -308,6 +310,6 @@ impl Client { /// Returns Some(&mut HeaderMap) when Client object is unique /// (No other clone of client exists at the same time). pub fn headers(&mut self) -> Option<&mut HeaderMap> { - Rc::get_mut(&mut self.0.headers) + Rc::get_mut(&mut self.0.default_headers) } } diff --git a/awc/src/middleware/redirect.rs b/awc/src/middleware/redirect.rs index 0fde48074..704d2d79d 100644 --- a/awc/src/middleware/redirect.rs +++ b/awc/src/middleware/redirect.rs @@ -442,13 +442,15 @@ mod tests { }); let client = ClientBuilder::new() - .header("custom", "value") + .add_default_header(("custom", "value")) .disable_redirects() .finish(); let res = client.get(srv.url("/")).send().await.unwrap(); assert_eq!(res.status().as_u16(), 302); - let client = ClientBuilder::new().header("custom", "value").finish(); + let client = ClientBuilder::new() + .add_default_header(("custom", "value")) + .finish(); let res = client.get(srv.url("/")).send().await.unwrap(); assert_eq!(res.status().as_u16(), 200); @@ -520,7 +522,7 @@ mod tests { // send a request to different origins, http://srv1/ then http://srv2/. So it should remove the header let client = ClientBuilder::new() - .header(header::AUTHORIZATION, "auth_key_value") + .add_default_header((header::AUTHORIZATION, "auth_key_value")) .finish(); let res = client.get(srv1.url("/")).send().await.unwrap(); assert_eq!(res.status().as_u16(), 200); diff --git a/awc/src/request.rs b/awc/src/request.rs index 3e1f83a82..9e37b2755 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -6,7 +6,7 @@ use serde::Serialize; use actix_http::{ error::HttpError, - header::{self, HeaderMap, HeaderValue, IntoHeaderPair}, + header::{self, HeaderMap, HeaderValue, TryIntoHeaderPair}, ConnectionType, Method, RequestHead, Uri, Version, }; @@ -147,11 +147,8 @@ impl ClientRequest { } /// Insert a header, replacing any that were set with an equivalent field name. - pub fn insert_header(mut self, header: H) -> Self - where - H: IntoHeaderPair, - { - match header.try_into_header_pair() { + pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self { + match header.try_into_pair() { Ok((key, value)) => { self.head.headers.insert(key, value); } @@ -162,11 +159,8 @@ impl ClientRequest { } /// Insert a header only if it is not yet set. - pub fn insert_header_if_none(mut self, header: H) -> Self - where - H: IntoHeaderPair, - { - match header.try_into_header_pair() { + pub fn insert_header_if_none(mut self, header: impl TryIntoHeaderPair) -> Self { + match header.try_into_pair() { Ok((key, value)) => { if !self.head.headers.contains_key(&key) { self.head.headers.insert(key, value); @@ -192,11 +186,8 @@ impl ClientRequest { /// .insert_header((CONTENT_TYPE, mime::APPLICATION_JSON)); /// # } /// ``` - pub fn append_header(mut self, header: H) -> Self - where - H: IntoHeaderPair, - { - match header.try_into_header_pair() { + pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self { + match header.try_into_pair() { Ok((key, value)) => self.head.headers.append(key, value), Err(e) => self.err = Some(e.into()), }; @@ -588,7 +579,7 @@ mod tests { #[actix_rt::test] async fn test_client_header() { let req = Client::builder() - .header(header::CONTENT_TYPE, "111") + .add_default_header((header::CONTENT_TYPE, "111")) .finish() .get("/"); @@ -606,7 +597,7 @@ mod tests { #[actix_rt::test] async fn test_client_header_override() { let req = Client::builder() - .header(header::CONTENT_TYPE, "111") + .add_default_header((header::CONTENT_TYPE, "111")) .finish() .get("/") .insert_header((header::CONTENT_TYPE, "222")); diff --git a/awc/src/sender.rs b/awc/src/sender.rs index 1faf6140a..f83a70a9b 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -10,7 +10,7 @@ use std::{ use actix_http::{ body::BodyStream, error::HttpError, - header::{self, HeaderMap, HeaderName, IntoHeaderValue}, + header::{self, HeaderMap, HeaderName, TryIntoHeaderValue}, RequestHead, RequestHeadType, }; use actix_rt::time::{sleep, Sleep}; @@ -298,7 +298,7 @@ impl RequestSender { fn set_header_if_none(&mut self, key: HeaderName, value: V) -> Result<(), HttpError> where - V: IntoHeaderValue, + V: TryIntoHeaderValue, { match self { RequestSender::Owned(head) => { diff --git a/awc/src/test.rs b/awc/src/test.rs index 4a5c8e7ea..1b41efc93 100644 --- a/awc/src/test.rs +++ b/awc/src/test.rs @@ -1,6 +1,6 @@ //! Test helpers for actix http client to use during testing. -use actix_http::{h1, header::IntoHeaderPair, Payload, ResponseHead, StatusCode, Version}; +use actix_http::{h1, header::TryIntoHeaderPair, Payload, ResponseHead, StatusCode, Version}; use bytes::Bytes; #[cfg(feature = "cookies")] @@ -28,10 +28,7 @@ impl Default for TestResponse { impl TestResponse { /// Create TestResponse and set header - pub fn with_header(header: H) -> Self - where - H: IntoHeaderPair, - { + pub fn with_header(header: impl TryIntoHeaderPair) -> Self { Self::default().insert_header(header) } @@ -42,11 +39,8 @@ impl TestResponse { } /// Insert a header - pub fn insert_header(mut self, header: H) -> Self - where - H: IntoHeaderPair, - { - if let Ok((key, value)) = header.try_into_header_pair() { + pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self { + if let Ok((key, value)) = header.try_into_pair() { self.head.headers.insert(key, value); return self; } @@ -54,11 +48,8 @@ impl TestResponse { } /// Append a header - pub fn append_header(mut self, header: H) -> Self - where - H: IntoHeaderPair, - { - if let Ok((key, value)) = header.try_into_header_pair() { + pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self { + if let Ok((key, value)) = header.try_into_pair() { self.head.headers.append(key, value); return self; } diff --git a/awc/src/ws.rs b/awc/src/ws.rs index f0d421dbc..06d54aadb 100644 --- a/awc/src/ws.rs +++ b/awc/src/ws.rs @@ -39,7 +39,7 @@ use crate::{ connect::{BoxedSocket, ConnectRequest}, error::{HttpError, InvalidUrl, SendRequestError, WsClientError}, http::{ - header::{self, HeaderName, HeaderValue, IntoHeaderValue, AUTHORIZATION}, + header::{self, HeaderName, HeaderValue, TryIntoHeaderValue, AUTHORIZATION}, ConnectionType, Method, StatusCode, Uri, Version, }, response::ClientResponse, @@ -171,7 +171,7 @@ impl WebsocketsRequest { where HeaderName: TryFrom, >::Error: Into, - V: IntoHeaderValue, + V: TryIntoHeaderValue, { match HeaderName::try_from(key) { Ok(key) => match value.try_into_value() { @@ -190,7 +190,7 @@ impl WebsocketsRequest { where HeaderName: TryFrom, >::Error: Into, - V: IntoHeaderValue, + V: TryIntoHeaderValue, { match HeaderName::try_from(key) { Ok(key) => match value.try_into_value() { @@ -209,7 +209,7 @@ impl WebsocketsRequest { where HeaderName: TryFrom, >::Error: Into, - V: IntoHeaderValue, + V: TryIntoHeaderValue, { match HeaderName::try_from(key) { Ok(key) => { @@ -445,7 +445,7 @@ mod tests { #[actix_rt::test] async fn test_header_override() { let req = Client::builder() - .header(header::CONTENT_TYPE, "111") + .add_default_header((header::CONTENT_TYPE, "111")) .finish() .ws("/") .set_header(header::CONTENT_TYPE, "222"); diff --git a/examples/basic.rs b/examples/basic.rs index d29546129..598d13a40 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -22,14 +22,14 @@ async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() - .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) + .wrap(middleware::DefaultHeaders::new().add(("X-Version", "0.2"))) .wrap(middleware::Compress::default()) .wrap(middleware::Logger::default()) .service(index) .service(no_params) .service( web::resource("/resource2/index.html") - .wrap(middleware::DefaultHeaders::new().header("X-Version-R2", "0.3")) + .wrap(middleware::DefaultHeaders::new().add(("X-Version-R2", "0.3"))) .default_service(web::route().to(HttpResponse::MethodNotAllowed)) .route(web::get().to(index_async)), ) diff --git a/examples/uds.rs b/examples/uds.rs index 1db252fef..cf0ffebde 100644 --- a/examples/uds.rs +++ b/examples/uds.rs @@ -26,14 +26,14 @@ async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() - .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) + .wrap(middleware::DefaultHeaders::new().add(("X-Version", "0.2"))) .wrap(middleware::Compress::default()) .wrap(middleware::Logger::default()) .service(index) .service(no_params) .service( web::resource("/resource2/index.html") - .wrap(middleware::DefaultHeaders::new().header("X-Version-R2", "0.3")) + .wrap(middleware::DefaultHeaders::new().add(("X-Version-R2", "0.3"))) .default_service(web::route().to(HttpResponse::MethodNotAllowed)) .route(web::get().to(index_async)), ) diff --git a/src/app.rs b/src/app.rs index 5323cb33a..feb35d7ae 100644 --- a/src/app.rs +++ b/src/app.rs @@ -602,7 +602,7 @@ mod tests { App::new() .wrap( DefaultHeaders::new() - .header(header::CONTENT_TYPE, HeaderValue::from_static("0001")), + .add((header::CONTENT_TYPE, HeaderValue::from_static("0001"))), ) .route("/test", web::get().to(HttpResponse::Ok)), ) @@ -623,7 +623,7 @@ mod tests { .route("/test", web::get().to(HttpResponse::Ok)) .wrap( DefaultHeaders::new() - .header(header::CONTENT_TYPE, HeaderValue::from_static("0001")), + .add((header::CONTENT_TYPE, HeaderValue::from_static("0001"))), ), ) .await; diff --git a/src/error/internal.rs b/src/error/internal.rs index b8e169018..37195dc2e 100644 --- a/src/error/internal.rs +++ b/src/error/internal.rs @@ -2,7 +2,7 @@ use std::{cell::RefCell, fmt, io::Write as _}; use actix_http::{ body::BoxBody, - header::{self, IntoHeaderValue as _}, + header::{self, TryIntoHeaderValue as _}, StatusCode, }; use bytes::{BufMut as _, BytesMut}; diff --git a/src/error/response_error.rs b/src/error/response_error.rs index 7260efa1a..e0b4af44c 100644 --- a/src/error/response_error.rs +++ b/src/error/response_error.rs @@ -8,7 +8,7 @@ use std::{ use actix_http::{ body::BoxBody, - header::{self, IntoHeaderValue}, + header::{self, TryIntoHeaderValue}, Response, StatusCode, }; use bytes::BytesMut; diff --git a/src/http/header/content_disposition.rs b/src/http/header/content_disposition.rs index 945a58f7f..26a9d8e76 100644 --- a/src/http/header/content_disposition.rs +++ b/src/http/header/content_disposition.rs @@ -14,7 +14,7 @@ use once_cell::sync::Lazy; use regex::Regex; use std::fmt::{self, Write}; -use super::{ExtendedValue, Header, IntoHeaderValue, Writer}; +use super::{ExtendedValue, Header, TryIntoHeaderValue, Writer}; use crate::http::header; /// Split at the index of the first `needle` if it exists or at the end. @@ -454,7 +454,7 @@ impl ContentDisposition { } } -impl IntoHeaderValue for ContentDisposition { +impl TryIntoHeaderValue for ContentDisposition { type Error = header::InvalidHeaderValue; fn try_into_value(self) -> Result { diff --git a/src/http/header/content_range.rs b/src/http/header/content_range.rs index 90b3f7fe2..bcbe77e66 100644 --- a/src/http/header/content_range.rs +++ b/src/http/header/content_range.rs @@ -3,7 +3,7 @@ use std::{ str::FromStr, }; -use super::{HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer, CONTENT_RANGE}; +use super::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue, Writer, CONTENT_RANGE}; use crate::error::ParseError; crate::http::header::common_header! { @@ -196,7 +196,7 @@ impl Display for ContentRangeSpec { } } -impl IntoHeaderValue for ContentRangeSpec { +impl TryIntoHeaderValue for ContentRangeSpec { type Error = InvalidHeaderValue; fn try_into_value(self) -> Result { diff --git a/src/http/header/entity.rs b/src/http/header/entity.rs index 50b40b7b2..76fe39f23 100644 --- a/src/http/header/entity.rs +++ b/src/http/header/entity.rs @@ -3,7 +3,7 @@ use std::{ str::FromStr, }; -use super::{HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer}; +use super::{HeaderValue, InvalidHeaderValue, TryIntoHeaderValue, Writer}; /// check that each char in the slice is either: /// 1. `%x21`, or @@ -159,7 +159,7 @@ impl FromStr for EntityTag { } } -impl IntoHeaderValue for EntityTag { +impl TryIntoHeaderValue for EntityTag { type Error = InvalidHeaderValue; fn try_into_value(self) -> Result { diff --git a/src/http/header/if_range.rs b/src/http/header/if_range.rs index 5af9255f6..b845fb3bf 100644 --- a/src/http/header/if_range.rs +++ b/src/http/header/if_range.rs @@ -1,8 +1,8 @@ use std::fmt::{self, Display, Write}; use super::{ - from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate, IntoHeaderValue, - InvalidHeaderValue, Writer, + from_one_raw_str, EntityTag, Header, HeaderName, HeaderValue, HttpDate, InvalidHeaderValue, + TryIntoHeaderValue, Writer, }; use crate::error::ParseError; use crate::http::header; @@ -96,7 +96,7 @@ impl Display for IfRange { } } -impl IntoHeaderValue for IfRange { +impl TryIntoHeaderValue for IfRange { type Error = InvalidHeaderValue; fn try_into_value(self) -> Result { diff --git a/src/http/header/macros.rs b/src/http/header/macros.rs index ca3792a37..25f40a52b 100644 --- a/src/http/header/macros.rs +++ b/src/http/header/macros.rs @@ -125,7 +125,7 @@ macro_rules! common_header { } } - impl $crate::http::header::IntoHeaderValue for $id { + impl $crate::http::header::TryIntoHeaderValue for $id { type Error = $crate::http::header::InvalidHeaderValue; #[inline] @@ -172,7 +172,7 @@ macro_rules! common_header { } } - impl $crate::http::header::IntoHeaderValue for $id { + impl $crate::http::header::TryIntoHeaderValue for $id { type Error = $crate::http::header::InvalidHeaderValue; #[inline] @@ -211,7 +211,7 @@ macro_rules! common_header { } } - impl $crate::http::header::IntoHeaderValue for $id { + impl $crate::http::header::TryIntoHeaderValue for $id { type Error = $crate::http::header::InvalidHeaderValue; #[inline] @@ -266,7 +266,7 @@ macro_rules! common_header { } } - impl $crate::http::header::IntoHeaderValue for $id { + impl $crate::http::header::TryIntoHeaderValue for $id { type Error = $crate::http::header::InvalidHeaderValue; #[inline] diff --git a/src/http/header/range.rs b/src/http/header/range.rs index c1d60f1ee..68028f53a 100644 --- a/src/http/header/range.rs +++ b/src/http/header/range.rs @@ -6,7 +6,7 @@ use std::{ use actix_http::{error::ParseError, header, HttpMessage}; -use super::{Header, HeaderName, HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer}; +use super::{Header, HeaderName, HeaderValue, InvalidHeaderValue, TryIntoHeaderValue, Writer}; /// `Range` header, defined /// in [RFC 7233 §3.1](https://datatracker.ietf.org/doc/html/rfc7233#section-3.1) @@ -274,7 +274,7 @@ impl Header for Range { } } -impl IntoHeaderValue for Range { +impl TryIntoHeaderValue for Range { type Error = InvalidHeaderValue; fn try_into_value(self) -> Result { diff --git a/src/lib.rs b/src/lib.rs index a44c9b3fb..171a2d101 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,7 +86,6 @@ pub mod middleware; mod request; mod request_data; mod resource; -mod responder; mod response; mod rmap; mod route; @@ -109,12 +108,10 @@ pub use crate::error::{Error, ResponseError, Result}; pub use crate::extract::FromRequest; pub use crate::request::HttpRequest; pub use crate::resource::Resource; -pub use crate::responder::Responder; -pub use crate::response::{HttpResponse, HttpResponseBuilder}; +pub use crate::response::{CustomizeResponder, HttpResponse, HttpResponseBuilder, Responder}; pub use crate::route::Route; pub use crate::scope::Scope; pub use crate::server::HttpServer; -// TODO: is exposing the error directly really needed -pub use crate::types::{Either, EitherExtractError}; +pub use crate::types::Either; pub(crate) type BoxError = Box; diff --git a/src/middleware/default_headers.rs b/src/middleware/default_headers.rs index dceca44c2..257467710 100644 --- a/src/middleware/default_headers.rs +++ b/src/middleware/default_headers.rs @@ -16,7 +16,7 @@ use pin_project_lite::pin_project; use crate::{ dev::{Service, Transform}, - http::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE}, + http::header::{HeaderMap, HeaderName, HeaderValue, TryIntoHeaderPair, CONTENT_TYPE}, service::{ServiceRequest, ServiceResponse}, Error, }; @@ -29,79 +29,81 @@ use crate::{ /// ``` /// use actix_web::{web, http, middleware, App, HttpResponse}; /// -/// fn main() { -/// let app = App::new() -/// .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) -/// .service( -/// web::resource("/test") -/// .route(web::get().to(|| HttpResponse::Ok())) -/// .route(web::method(http::Method::HEAD).to(|| HttpResponse::MethodNotAllowed())) -/// ); -/// } +/// let app = App::new() +/// .wrap(middleware::DefaultHeaders::new().add(("X-Version", "0.2"))) +/// .service( +/// web::resource("/test") +/// .route(web::get().to(|| HttpResponse::Ok())) +/// .route(web::method(http::Method::HEAD).to(|| HttpResponse::MethodNotAllowed())) +/// ); /// ``` -#[derive(Clone)] +#[derive(Debug, Clone, Default)] pub struct DefaultHeaders { inner: Rc, } +#[derive(Debug, Default)] struct Inner { headers: HeaderMap, } -impl Default for DefaultHeaders { - fn default() -> Self { - DefaultHeaders { - inner: Rc::new(Inner { - headers: HeaderMap::new(), - }), - } - } -} - impl DefaultHeaders { /// Constructs an empty `DefaultHeaders` middleware. + #[inline] pub fn new() -> DefaultHeaders { DefaultHeaders::default() } /// Adds a header to the default set. - #[inline] - pub fn header(mut self, key: K, value: V) -> Self + /// + /// # Panics + /// Panics when resolved header name or value is invalid. + #[allow(clippy::should_implement_trait)] + pub fn add(mut self, header: impl TryIntoHeaderPair) -> Self { + // standard header terminology `insert` or `append` for this method would make the behavior + // of this middleware less obvious since it only adds the headers if they are not present + + match header.try_into_pair() { + Ok((key, value)) => Rc::get_mut(&mut self.inner) + .expect("All default headers must be added before cloning.") + .headers + .append(key, value), + Err(err) => panic!("Invalid header: {}", err.into()), + } + + self + } + + #[doc(hidden)] + #[deprecated( + since = "4.0.0", + note = "Prefer `.add((key, value))`. Will be removed in v5." + )] + pub fn header(self, key: K, value: V) -> Self where HeaderName: TryFrom, >::Error: Into, HeaderValue: TryFrom, >::Error: Into, { - #[allow(clippy::match_wild_err_arm)] - match HeaderName::try_from(key) { - Ok(key) => match HeaderValue::try_from(value) { - Ok(value) => { - Rc::get_mut(&mut self.inner) - .expect("Multiple copies exist") - .headers - .append(key, value); - } - Err(_) => panic!("Can not create header value"), - }, - Err(_) => panic!("Can not create header name"), - } - self + self.add(( + HeaderName::try_from(key) + .map_err(Into::into) + .expect("Invalid header name"), + HeaderValue::try_from(value) + .map_err(Into::into) + .expect("Invalid header value"), + )) } /// Adds a default *Content-Type* header if response does not contain one. /// /// Default is `application/octet-stream`. - pub fn add_content_type(mut self) -> Self { - Rc::get_mut(&mut self.inner) - .expect("Multiple `Inner` copies exist.") - .headers - .insert( - CONTENT_TYPE, - HeaderValue::from_static("application/octet-stream"), - ); - - self + pub fn add_content_type(self) -> Self { + self.add(( + CONTENT_TYPE, + HeaderValue::from_static("application/octet-stream"), + )) } } @@ -119,7 +121,7 @@ where fn new_transform(&self, service: S) -> Self::Future { ready(Ok(DefaultHeadersMiddleware { service, - inner: self.inner.clone(), + inner: Rc::clone(&self.inner), })) } } @@ -197,17 +199,22 @@ mod tests { }; #[actix_rt::test] - async fn test_default_headers() { + async fn adding_default_headers() { let mw = DefaultHeaders::new() - .header(CONTENT_TYPE, "0001") + .add(("X-TEST", "0001")) + .add(("X-TEST-TWO", HeaderValue::from_static("123"))) .new_transform(ok_service()) .await .unwrap(); let req = TestRequest::default().to_srv_request(); - let resp = mw.call(req).await.unwrap(); - assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); + let res = mw.call(req).await.unwrap(); + assert_eq!(res.headers().get("x-test").unwrap(), "0001"); + assert_eq!(res.headers().get("x-test-two").unwrap(), "123"); + } + #[actix_rt::test] + async fn no_override_existing() { let req = TestRequest::default().to_srv_request(); let srv = |req: ServiceRequest| { ok(req.into_response( @@ -217,7 +224,7 @@ mod tests { )) }; let mw = DefaultHeaders::new() - .header(CONTENT_TYPE, "0001") + .add((CONTENT_TYPE, "0001")) .new_transform(srv.into_service()) .await .unwrap(); @@ -226,7 +233,7 @@ mod tests { } #[actix_rt::test] - async fn test_content_type() { + async fn adding_content_type() { let srv = |req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())); let mw = DefaultHeaders::new() .add_content_type() @@ -241,4 +248,16 @@ mod tests { "application/octet-stream" ); } + + #[test] + #[should_panic] + fn invalid_header_name() { + DefaultHeaders::new().add((":", "hello")); + } + + #[test] + #[should_panic] + fn invalid_header_value() { + DefaultHeaders::new().add(("x-test", "\n")); + } } diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index d19cb64e9..42d285580 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -33,7 +33,7 @@ mod tests { let _ = App::new() .wrap(Compat::new(Logger::default())) .wrap(Condition::new(true, DefaultHeaders::new())) - .wrap(DefaultHeaders::new().header("X-Test2", "X-Value2")) + .wrap(DefaultHeaders::new().add(("X-Test2", "X-Value2"))) .wrap(ErrorHandlers::new().handler(StatusCode::FORBIDDEN, |res| { Ok(ErrorHandlerResponse::Response(res)) })) @@ -46,7 +46,7 @@ mod tests { .wrap(ErrorHandlers::new().handler(StatusCode::FORBIDDEN, |res| { Ok(ErrorHandlerResponse::Response(res)) })) - .wrap(DefaultHeaders::new().header("X-Test2", "X-Value2")) + .wrap(DefaultHeaders::new().add(("X-Test2", "X-Value2"))) .wrap(Condition::new(true, DefaultHeaders::new())) .wrap(Compat::new(Logger::default())); diff --git a/src/request_data.rs b/src/request_data.rs index 680f3e566..b685fd0d6 100644 --- a/src/request_data.rs +++ b/src/request_data.rs @@ -17,7 +17,7 @@ use crate::{dev::Payload, error::ErrorInternalServerError, Error, FromRequest, H /// # Mutating Request Data /// Note that since extractors must output owned data, only types that `impl Clone` can use this /// extractor. A clone is taken of the required request data and can, therefore, not be directly -/// mutated in-place. To mutate request data, continue to use [`HttpRequest::extensions_mut`] or +/// mutated in-place. To mutate request data, continue to use [`HttpRequest::req_data_mut`] or /// re-insert the cloned data back into the extensions map. A `DerefMut` impl is intentionally not /// provided to make this potential foot-gun more obvious. /// diff --git a/src/resource.rs b/src/resource.rs index 420374a86..53104930a 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -15,13 +15,12 @@ use crate::{ dev::{ensure_leading_slash, AppService, ResourceDef}, guard::Guard, handler::Handler, - responder::Responder, route::{Route, RouteService}, service::{ BoxedHttpService, BoxedHttpServiceFactory, HttpServiceFactory, ServiceRequest, ServiceResponse, }, - BoxError, Error, FromRequest, HttpResponse, + BoxError, Error, FromRequest, HttpResponse, Responder, }; /// *Resource* is an entry in resources table which corresponds to requested URL. @@ -526,7 +525,7 @@ mod tests { .name("test") .wrap( DefaultHeaders::new() - .header(header::CONTENT_TYPE, HeaderValue::from_static("0001")), + .add((header::CONTENT_TYPE, HeaderValue::from_static("0001"))), ) .route(web::get().to(HttpResponse::Ok)), ), diff --git a/src/response/builder.rs b/src/response/builder.rs index 18a1c8a7f..b500ab331 100644 --- a/src/response/builder.rs +++ b/src/response/builder.rs @@ -9,7 +9,7 @@ use std::{ use actix_http::{ body::{BodyStream, BoxBody, MessageBody}, error::HttpError, - header::{self, HeaderName, IntoHeaderPair, IntoHeaderValue}, + header::{self, HeaderName, TryIntoHeaderPair, TryIntoHeaderValue}, ConnectionType, Extensions, Response, ResponseHead, StatusCode, }; use bytes::Bytes; @@ -67,12 +67,9 @@ impl HttpResponseBuilder { /// .insert_header(("X-TEST", "value")) /// .finish(); /// ``` - pub fn insert_header(&mut self, header: H) -> &mut Self - where - H: IntoHeaderPair, - { + pub fn insert_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self { if let Some(parts) = self.inner() { - match header.try_into_header_pair() { + match header.try_into_pair() { Ok((key, value)) => { parts.headers.insert(key, value); } @@ -94,12 +91,9 @@ impl HttpResponseBuilder { /// .append_header(("X-TEST", "value2")) /// .finish(); /// ``` - pub fn append_header(&mut self, header: H) -> &mut Self - where - H: IntoHeaderPair, - { + pub fn append_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self { if let Some(parts) = self.inner() { - match header.try_into_header_pair() { + match header.try_into_pair() { Ok((key, value)) => parts.headers.append(key, value), Err(e) => self.err = Some(e.into()), }; @@ -118,7 +112,7 @@ impl HttpResponseBuilder { where K: TryInto, K::Error: Into, - V: IntoHeaderValue, + V: TryIntoHeaderValue, { if self.err.is_some() { return self; @@ -143,7 +137,7 @@ impl HttpResponseBuilder { where K: TryInto, K::Error: Into, - V: IntoHeaderValue, + V: TryIntoHeaderValue, { if self.err.is_some() { return self; @@ -180,7 +174,7 @@ impl HttpResponseBuilder { #[inline] pub fn upgrade(&mut self, value: V) -> &mut Self where - V: IntoHeaderValue, + V: TryIntoHeaderValue, { if let Some(parts) = self.inner() { parts.set_connection_type(ConnectionType::Upgrade); @@ -218,7 +212,7 @@ impl HttpResponseBuilder { #[inline] pub fn content_type(&mut self, value: V) -> &mut Self where - V: IntoHeaderValue, + V: TryIntoHeaderValue, { if let Some(parts) = self.inner() { match value.try_into_value() { diff --git a/src/response/customize_responder.rs b/src/response/customize_responder.rs new file mode 100644 index 000000000..11f6b2916 --- /dev/null +++ b/src/response/customize_responder.rs @@ -0,0 +1,245 @@ +use actix_http::{ + body::{EitherBody, MessageBody}, + error::HttpError, + header::HeaderMap, + header::TryIntoHeaderPair, + StatusCode, +}; + +use crate::{BoxError, HttpRequest, HttpResponse, Responder}; + +/// Allows overriding status code and headers for a [`Responder`]. +/// +/// Created by the [`Responder::customize`] method. +pub struct CustomizeResponder { + inner: CustomizeResponderInner, + error: Option, +} + +struct CustomizeResponderInner { + responder: R, + status: Option, + override_headers: HeaderMap, + append_headers: HeaderMap, +} + +impl CustomizeResponder { + pub(crate) fn new(responder: R) -> Self { + CustomizeResponder { + inner: CustomizeResponderInner { + responder, + status: None, + override_headers: HeaderMap::new(), + append_headers: HeaderMap::new(), + }, + error: None, + } + } + + /// Override a status code for the Responder's response. + /// + /// # Examples + /// ``` + /// use actix_web::{Responder, http::StatusCode, test::TestRequest}; + /// + /// let responder = "Welcome!".customize().with_status(StatusCode::ACCEPTED); + /// + /// let request = TestRequest::default().to_http_request(); + /// let response = responder.respond_to(&request); + /// assert_eq!(response.status(), StatusCode::ACCEPTED); + /// ``` + pub fn with_status(mut self, status: StatusCode) -> Self { + if let Some(inner) = self.inner() { + inner.status = Some(status); + } + + self + } + + /// Insert (override) header in the final response. + /// + /// Overrides other headers with the same name. + /// See [`HeaderMap::insert`](crate::http::header::HeaderMap::insert). + /// + /// Headers added with this method will be inserted before those added + /// with [`append_header`](Self::append_header). As such, header(s) can be overridden with more + /// than one new header by first calling `insert_header` followed by `append_header`. + /// + /// # Examples + /// ``` + /// use actix_web::{Responder, test::TestRequest}; + /// + /// let responder = "Hello world!" + /// .customize() + /// .insert_header(("x-version", "1.2.3")); + /// + /// let request = TestRequest::default().to_http_request(); + /// let response = responder.respond_to(&request); + /// assert_eq!(response.headers().get("x-version").unwrap(), "1.2.3"); + /// ``` + pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self { + if let Some(inner) = self.inner() { + match header.try_into_pair() { + Ok((key, value)) => { + inner.override_headers.insert(key, value); + } + Err(err) => self.error = Some(err.into()), + }; + } + + self + } + + /// Append header to the final response. + /// + /// Unlike [`insert_header`](Self::insert_header), this will not override existing headers. + /// See [`HeaderMap::append`](crate::http::header::HeaderMap::append). + /// + /// Headers added here are appended _after_ additions/overrides from `insert_header`. + /// + /// # Examples + /// ``` + /// use actix_web::{Responder, test::TestRequest}; + /// + /// let responder = "Hello world!" + /// .customize() + /// .append_header(("x-version", "1.2.3")); + /// + /// let request = TestRequest::default().to_http_request(); + /// let response = responder.respond_to(&request); + /// assert_eq!(response.headers().get("x-version").unwrap(), "1.2.3"); + /// ``` + pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self { + if let Some(inner) = self.inner() { + match header.try_into_pair() { + Ok((key, value)) => { + inner.append_headers.append(key, value); + } + Err(err) => self.error = Some(err.into()), + }; + } + + self + } + + #[doc(hidden)] + #[deprecated(since = "4.0.0", note = "Renamed to `insert_header`.")] + pub fn with_header(self, header: impl TryIntoHeaderPair) -> Self + where + Self: Sized, + { + self.insert_header(header) + } + + fn inner(&mut self) -> Option<&mut CustomizeResponderInner> { + if self.error.is_some() { + None + } else { + Some(&mut self.inner) + } + } +} + +impl Responder for CustomizeResponder +where + T: Responder, + ::Error: Into, +{ + type Body = EitherBody; + + fn respond_to(self, req: &HttpRequest) -> HttpResponse { + if let Some(err) = self.error { + return HttpResponse::from_error(err).map_into_right_body(); + } + + let mut res = self.inner.responder.respond_to(req); + + if let Some(status) = self.inner.status { + *res.status_mut() = status; + } + + for (k, v) in self.inner.override_headers { + res.headers_mut().insert(k, v); + } + + for (k, v) in self.inner.append_headers { + res.headers_mut().append(k, v); + } + + res.map_into_left_body() + } +} + +#[cfg(test)] +mod tests { + use bytes::Bytes; + + use actix_http::body::to_bytes; + + use super::*; + use crate::{ + http::{ + header::{HeaderValue, CONTENT_TYPE}, + StatusCode, + }, + test::TestRequest, + }; + + #[actix_rt::test] + async fn customize_responder() { + let req = TestRequest::default().to_http_request(); + let res = "test" + .to_string() + .customize() + .with_status(StatusCode::BAD_REQUEST) + .respond_to(&req); + + assert_eq!(res.status(), StatusCode::BAD_REQUEST); + assert_eq!( + to_bytes(res.into_body()).await.unwrap(), + Bytes::from_static(b"test"), + ); + + let res = "test" + .to_string() + .customize() + .insert_header(("content-type", "json")) + .respond_to(&req); + + assert_eq!(res.status(), StatusCode::OK); + assert_eq!( + res.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("json") + ); + assert_eq!( + to_bytes(res.into_body()).await.unwrap(), + Bytes::from_static(b"test"), + ); + } + + #[actix_rt::test] + async fn tuple_responder_with_status_code() { + let req = TestRequest::default().to_http_request(); + let res = ("test".to_string(), StatusCode::BAD_REQUEST).respond_to(&req); + assert_eq!(res.status(), StatusCode::BAD_REQUEST); + assert_eq!( + to_bytes(res.into_body()).await.unwrap(), + Bytes::from_static(b"test"), + ); + + let req = TestRequest::default().to_http_request(); + let res = ("test".to_string(), StatusCode::OK) + .customize() + .insert_header((CONTENT_TYPE, mime::APPLICATION_JSON)) + .respond_to(&req); + assert_eq!(res.status(), StatusCode::OK); + assert_eq!( + res.headers().get(CONTENT_TYPE).unwrap(), + HeaderValue::from_static("application/json") + ); + assert_eq!( + to_bytes(res.into_body()).await.unwrap(), + Bytes::from_static(b"test"), + ); + } +} diff --git a/src/response/mod.rs b/src/response/mod.rs index 8401db9d2..977147104 100644 --- a/src/response/mod.rs +++ b/src/response/mod.rs @@ -1,9 +1,13 @@ mod builder; +mod customize_responder; mod http_codes; +mod responder; #[allow(clippy::module_inception)] mod response; pub use self::builder::HttpResponseBuilder; +pub use self::customize_responder::CustomizeResponder; +pub use self::responder::Responder; pub use self::response::HttpResponse; #[cfg(feature = "cookies")] diff --git a/src/responder.rs b/src/response/responder.rs similarity index 63% rename from src/responder.rs rename to src/response/responder.rs index e72739a71..319b824f1 100644 --- a/src/responder.rs +++ b/src/response/responder.rs @@ -2,64 +2,58 @@ use std::borrow::Cow; use actix_http::{ body::{BoxBody, EitherBody, MessageBody}, - error::HttpError, - header::HeaderMap, - header::IntoHeaderPair, + header::TryIntoHeaderPair, StatusCode, }; use bytes::{Bytes, BytesMut}; use crate::{BoxError, Error, HttpRequest, HttpResponse, HttpResponseBuilder}; +use super::CustomizeResponder; + /// Trait implemented by types that can be converted to an HTTP response. /// /// Any types that implement this trait can be used in the return type of a handler. +// # TODO: more about implementation notes and foreign impls pub trait Responder { type Body: MessageBody + 'static; /// Convert self to `HttpResponse`. fn respond_to(self, req: &HttpRequest) -> HttpResponse; - /// Override a status code for a Responder. + /// Wraps responder to allow alteration of its response. /// - /// ``` - /// use actix_web::{http::StatusCode, HttpRequest, Responder}; + /// See [`CustomizeResponder`] docs for its capabilities. /// - /// fn index(req: HttpRequest) -> impl Responder { - /// "Welcome!".with_status(StatusCode::OK) - /// } + /// # Examples /// ``` - fn with_status(self, status: StatusCode) -> CustomResponder + /// use actix_web::{Responder, http::StatusCode, test::TestRequest}; + /// + /// let responder = "Hello world!" + /// .customize() + /// .with_status(StatusCode::BAD_REQUEST) + /// .insert_header(("x-hello", "world")); + /// + /// let request = TestRequest::default().to_http_request(); + /// let response = responder.respond_to(&request); + /// assert_eq!(response.status(), StatusCode::BAD_REQUEST); + /// assert_eq!(response.headers().get("x-hello").unwrap(), "world"); + /// ``` + #[inline] + fn customize(self) -> CustomizeResponder where Self: Sized, { - CustomResponder::new(self).with_status(status) + CustomizeResponder::new(self) } - /// Insert header to the final response. - /// - /// Overrides other headers with the same name. - /// - /// ``` - /// use actix_web::{web, HttpRequest, Responder}; - /// use serde::Serialize; - /// - /// #[derive(Serialize)] - /// struct MyObj { - /// name: String, - /// } - /// - /// fn index(req: HttpRequest) -> impl Responder { - /// web::Json(MyObj { name: "Name".to_owned() }) - /// .with_header(("x-version", "1.2.3")) - /// } - /// ``` - fn with_header(self, header: H) -> CustomResponder + #[doc(hidden)] + #[deprecated(since = "4.0.0", note = "Prefer `.customize().insert_header(header)`.")] + fn with_header(self, header: impl TryIntoHeaderPair) -> CustomizeResponder where Self: Sized, - H: IntoHeaderPair, { - CustomResponder::new(self).with_header(header) + self.customize().insert_header(header) } } @@ -181,98 +175,6 @@ macro_rules! impl_into_string_responder { impl_into_string_responder!(&'_ String); impl_into_string_responder!(Cow<'_, str>); -/// Allows overriding status code and headers for a responder. -pub struct CustomResponder { - responder: T, - status: Option, - headers: Result, -} - -impl CustomResponder { - fn new(responder: T) -> Self { - CustomResponder { - responder, - status: None, - headers: Ok(HeaderMap::new()), - } - } - - /// Override a status code for the Responder's response. - /// - /// ``` - /// use actix_web::{HttpRequest, Responder, http::StatusCode}; - /// - /// fn index(req: HttpRequest) -> impl Responder { - /// "Welcome!".with_status(StatusCode::OK) - /// } - /// ``` - pub fn with_status(mut self, status: StatusCode) -> Self { - self.status = Some(status); - self - } - - /// Insert header to the final response. - /// - /// Overrides other headers with the same name. - /// - /// ``` - /// use actix_web::{web, HttpRequest, Responder}; - /// use serde::Serialize; - /// - /// #[derive(Serialize)] - /// struct MyObj { - /// name: String, - /// } - /// - /// fn index(req: HttpRequest) -> impl Responder { - /// web::Json(MyObj { name: "Name".to_string() }) - /// .with_header(("x-version", "1.2.3")) - /// .with_header(("x-version", "1.2.3")) - /// } - /// ``` - pub fn with_header(mut self, header: H) -> Self - where - H: IntoHeaderPair, - { - if let Ok(ref mut headers) = self.headers { - match header.try_into_header_pair() { - Ok((key, value)) => headers.append(key, value), - Err(e) => self.headers = Err(e.into()), - }; - } - - self - } -} - -impl Responder for CustomResponder -where - T: Responder, - ::Error: Into, -{ - type Body = EitherBody; - - fn respond_to(self, req: &HttpRequest) -> HttpResponse { - let headers = match self.headers { - Ok(headers) => headers, - Err(err) => return HttpResponse::from_error(err).map_into_right_body(), - }; - - let mut res = self.responder.respond_to(req); - - if let Some(status) = self.status { - *res.status_mut() = status; - } - - for (k, v) in headers { - // TODO: before v4, decide if this should be append instead - res.headers_mut().insert(k, v); - } - - res.map_into_left_body() - } -} - #[cfg(test)] pub(crate) mod tests { use actix_service::Service; @@ -440,59 +342,4 @@ pub(crate) mod tests { assert_eq!(res.status(), StatusCode::BAD_REQUEST); } - - #[actix_rt::test] - async fn test_custom_responder() { - let req = TestRequest::default().to_http_request(); - let res = "test" - .to_string() - .with_status(StatusCode::BAD_REQUEST) - .respond_to(&req); - - assert_eq!(res.status(), StatusCode::BAD_REQUEST); - assert_eq!( - to_bytes(res.into_body()).await.unwrap(), - Bytes::from_static(b"test"), - ); - - let res = "test" - .to_string() - .with_header(("content-type", "json")) - .respond_to(&req); - - assert_eq!(res.status(), StatusCode::OK); - assert_eq!( - res.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("json") - ); - assert_eq!( - to_bytes(res.into_body()).await.unwrap(), - Bytes::from_static(b"test"), - ); - } - - #[actix_rt::test] - async fn test_tuple_responder_with_status_code() { - let req = TestRequest::default().to_http_request(); - let res = ("test".to_string(), StatusCode::BAD_REQUEST).respond_to(&req); - assert_eq!(res.status(), StatusCode::BAD_REQUEST); - assert_eq!( - to_bytes(res.into_body()).await.unwrap(), - Bytes::from_static(b"test"), - ); - - let req = TestRequest::default().to_http_request(); - let res = ("test".to_string(), StatusCode::OK) - .with_header((CONTENT_TYPE, mime::APPLICATION_JSON)) - .respond_to(&req); - assert_eq!(res.status(), StatusCode::OK); - assert_eq!( - res.headers().get(CONTENT_TYPE).unwrap(), - HeaderValue::from_static("application/json") - ); - assert_eq!( - to_bytes(res.into_body()).await.unwrap(), - Bytes::from_static(b"test"), - ); - } } diff --git a/src/scope.rs b/src/scope.rs index 74523cd94..c35584770 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -935,7 +935,7 @@ mod tests { web::scope("app") .wrap( DefaultHeaders::new() - .header(header::CONTENT_TYPE, HeaderValue::from_static("0001")), + .add((header::CONTENT_TYPE, HeaderValue::from_static("0001"))), ) .service(web::resource("/test").route(web::get().to(HttpResponse::Ok))), ), diff --git a/src/test.rs b/src/test.rs index cfb3ef8f2..5ef2343a8 100644 --- a/src/test.rs +++ b/src/test.rs @@ -4,8 +4,8 @@ use std::{borrow::Cow, net::SocketAddr, rc::Rc}; pub use actix_http::test::TestBuffer; use actix_http::{ - header::IntoHeaderPair, test::TestRequest as HttpTestRequest, Extensions, Method, Request, - StatusCode, Uri, Version, + header::TryIntoHeaderPair, test::TestRequest as HttpTestRequest, Extensions, Method, + Request, StatusCode, Uri, Version, }; use actix_router::{Path, ResourceDef, Url}; use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory}; @@ -445,19 +445,13 @@ impl TestRequest { } /// Insert a header, replacing any that were set with an equivalent field name. - pub fn insert_header(mut self, header: H) -> Self - where - H: IntoHeaderPair, - { + pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self { self.req.insert_header(header); self } /// Append a header, keeping any that were set with an equivalent field name. - pub fn append_header(mut self, header: H) -> Self - where - H: IntoHeaderPair, - { + pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self { self.req.append_header(header); self } diff --git a/src/web.rs b/src/web.rs index 16dbace60..042b8a008 100644 --- a/src/web.rs +++ b/src/web.rs @@ -8,7 +8,7 @@ pub use bytes::{Buf, BufMut, Bytes, BytesMut}; use crate::{ body::MessageBody, error::BlockingError, extract::FromRequest, handler::Handler, - resource::Resource, responder::Responder, route::Route, scope::Scope, service::WebService, + resource::Resource, route::Route, scope::Scope, service::WebService, Responder, }; pub use crate::config::ServiceConfig; From fb091b2b88f9590d449415f272bd763d3ad4df3c Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 14 Dec 2021 18:33:17 +0000 Subject: [PATCH 29/87] split up router pattern and resource_path modules --- actix-router/src/lib.rs | 142 ++---------------------------- actix-router/src/pattern.rs | 92 +++++++++++++++++++ actix-router/src/resource_path.rs | 36 ++++++++ 3 files changed, 137 insertions(+), 133 deletions(-) create mode 100644 actix-router/src/pattern.rs create mode 100644 actix-router/src/resource_path.rs diff --git a/actix-router/src/lib.rs b/actix-router/src/lib.rs index f616f7fc6..03f464626 100644 --- a/actix-router/src/lib.rs +++ b/actix-router/src/lib.rs @@ -7,144 +7,20 @@ mod de; mod path; +mod pattern; mod resource; +mod resource_path; mod router; -pub use self::de::PathDeserializer; -pub use self::path::Path; -pub use self::resource::ResourceDef; -pub use self::router::{ResourceInfo, Router, RouterBuilder}; - -// TODO: this trait is necessary, document it -// see impl Resource for ServiceRequest -pub trait Resource { - fn resource_path(&mut self) -> &mut Path; -} - -pub trait ResourcePath { - fn path(&self) -> &str; -} - -impl ResourcePath for String { - fn path(&self) -> &str { - self.as_str() - } -} - -impl<'a> ResourcePath for &'a str { - fn path(&self) -> &str { - self - } -} - -impl ResourcePath for bytestring::ByteString { - fn path(&self) -> &str { - &*self - } -} - -/// One or many patterns. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Patterns { - Single(String), - List(Vec), -} - -impl Patterns { - pub fn is_empty(&self) -> bool { - match self { - Patterns::Single(_) => false, - Patterns::List(pats) => pats.is_empty(), - } - } -} - -/// Helper trait for type that could be converted to one or more path pattern. -pub trait IntoPatterns { - fn patterns(&self) -> Patterns; -} - -impl IntoPatterns for String { - fn patterns(&self) -> Patterns { - Patterns::Single(self.clone()) - } -} - -impl<'a> IntoPatterns for &'a String { - fn patterns(&self) -> Patterns { - Patterns::Single((*self).clone()) - } -} - -impl<'a> IntoPatterns for &'a str { - fn patterns(&self) -> Patterns { - Patterns::Single((*self).to_owned()) - } -} - -impl IntoPatterns for bytestring::ByteString { - fn patterns(&self) -> Patterns { - Patterns::Single(self.to_string()) - } -} - -impl IntoPatterns for Patterns { - fn patterns(&self) -> Patterns { - self.clone() - } -} - -impl> IntoPatterns for Vec { - fn patterns(&self) -> Patterns { - let mut patterns = self.iter().map(|v| v.as_ref().to_owned()); - - match patterns.size_hint() { - (1, _) => Patterns::Single(patterns.next().unwrap()), - _ => Patterns::List(patterns.collect()), - } - } -} - -macro_rules! array_patterns_single (($tp:ty) => { - impl IntoPatterns for [$tp; 1] { - fn patterns(&self) -> Patterns { - Patterns::Single(self[0].to_owned()) - } - } -}); - -macro_rules! array_patterns_multiple (($tp:ty, $str_fn:expr, $($num:tt) +) => { - // for each array length specified in $num - $( - impl IntoPatterns for [$tp; $num] { - fn patterns(&self) -> Patterns { - Patterns::List(self.iter().map($str_fn).collect()) - } - } - )+ -}); - -array_patterns_single!(&str); -array_patterns_multiple!(&str, |&v| v.to_owned(), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16); - -array_patterns_single!(String); -array_patterns_multiple!(String, |v| v.clone(), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16); - #[cfg(feature = "http")] mod url; +pub use self::de::PathDeserializer; +pub use self::path::Path; +pub use self::pattern::{IntoPatterns, Patterns}; +pub use self::resource::ResourceDef; +pub use self::resource_path::{Resource, ResourcePath}; +pub use self::router::{ResourceInfo, Router, RouterBuilder}; + #[cfg(feature = "http")] pub use self::url::{Quoter, Url}; - -#[cfg(feature = "http")] -mod http_impls { - use http::Uri; - - use super::ResourcePath; - - impl ResourcePath for Uri { - fn path(&self) -> &str { - self.path() - } - } -} diff --git a/actix-router/src/pattern.rs b/actix-router/src/pattern.rs new file mode 100644 index 000000000..78a638a78 --- /dev/null +++ b/actix-router/src/pattern.rs @@ -0,0 +1,92 @@ +/// One or many patterns. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Patterns { + Single(String), + List(Vec), +} + +impl Patterns { + pub fn is_empty(&self) -> bool { + match self { + Patterns::Single(_) => false, + Patterns::List(pats) => pats.is_empty(), + } + } +} + +/// Helper trait for type that could be converted to one or more path patterns. +pub trait IntoPatterns { + fn patterns(&self) -> Patterns; +} + +impl IntoPatterns for String { + fn patterns(&self) -> Patterns { + Patterns::Single(self.clone()) + } +} + +impl IntoPatterns for &String { + fn patterns(&self) -> Patterns { + (*self).patterns() + } +} + +impl IntoPatterns for str { + fn patterns(&self) -> Patterns { + Patterns::Single(self.to_owned()) + } +} + +impl IntoPatterns for &str { + fn patterns(&self) -> Patterns { + (*self).patterns() + } +} + +impl IntoPatterns for bytestring::ByteString { + fn patterns(&self) -> Patterns { + Patterns::Single(self.to_string()) + } +} + +impl IntoPatterns for Patterns { + fn patterns(&self) -> Patterns { + self.clone() + } +} + +impl> IntoPatterns for Vec { + fn patterns(&self) -> Patterns { + let mut patterns = self.iter().map(|v| v.as_ref().to_owned()); + + match patterns.size_hint() { + (1, _) => Patterns::Single(patterns.next().unwrap()), + _ => Patterns::List(patterns.collect()), + } + } +} + +macro_rules! array_patterns_single (($tp:ty) => { + impl IntoPatterns for [$tp; 1] { + fn patterns(&self) -> Patterns { + Patterns::Single(self[0].to_owned()) + } + } +}); + +macro_rules! array_patterns_multiple (($tp:ty, $str_fn:expr, $($num:tt) +) => { + // for each array length specified in space-separated $num + $( + impl IntoPatterns for [$tp; $num] { + fn patterns(&self) -> Patterns { + Patterns::List(self.iter().map($str_fn).collect()) + } + } + )+ +}); + +array_patterns_single!(&str); +array_patterns_multiple!(&str, |&v| v.to_owned(), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16); + +array_patterns_single!(String); +array_patterns_multiple!(String, |v| v.clone(), 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16); diff --git a/actix-router/src/resource_path.rs b/actix-router/src/resource_path.rs new file mode 100644 index 000000000..91a7f2f55 --- /dev/null +++ b/actix-router/src/resource_path.rs @@ -0,0 +1,36 @@ +use crate::Path; + +// TODO: this trait is necessary, document it +// see impl Resource for ServiceRequest +pub trait Resource { + fn resource_path(&mut self) -> &mut Path; +} + +pub trait ResourcePath { + fn path(&self) -> &str; +} + +impl ResourcePath for String { + fn path(&self) -> &str { + self.as_str() + } +} + +impl<'a> ResourcePath for &'a str { + fn path(&self) -> &str { + self + } +} + +impl ResourcePath for bytestring::ByteString { + fn path(&self) -> &str { + &*self + } +} + +#[cfg(feature = "http")] +impl ResourcePath for http::Uri { + fn path(&self) -> &str { + self.path() + } +} From 05255c7f7c92d785bac919f39374efa9985eec57 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 14 Dec 2021 19:57:18 +0000 Subject: [PATCH 30/87] remove either crate conversions (#2516) --- CHANGES.md | 2 ++ Cargo.toml | 1 - src/types/either.rs | 25 ++++--------------------- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2df820027..b8d3ce8de 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,8 +10,10 @@ ### Removed * Top-level `EitherExtractError` export. [#2510] +* Conversion implementations for `either` crate. [#2516] [#2510]: https://github.com/actix/actix-web/pull/2510 +[#2516]: https://github.com/actix/actix-web/pull/2516 ## 4.0.0-beta.14 - 2021-12-11 diff --git a/Cargo.toml b/Cargo.toml index 96e2dd797..e20529e1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,6 @@ bytes = "1" cfg-if = "1" cookie = { version = "0.15", features = ["percent-encode"], optional = true } derive_more = "0.99.5" -either = "1.5.3" encoding_rs = "0.8" futures-core = { version = "0.3.7", default-features = false } futures-util = { version = "0.3.7", default-features = false } diff --git a/src/types/either.rs b/src/types/either.rs index 3c759736e..5b8e02525 100644 --- a/src/types/either.rs +++ b/src/types/either.rs @@ -12,7 +12,8 @@ use futures_core::ready; use pin_project_lite::pin_project; use crate::{ - body, dev, + body::EitherBody, + dev, web::{Form, Json}, Error, FromRequest, HttpRequest, HttpResponse, Responder, }; @@ -101,24 +102,6 @@ impl Either, Form> { } } -impl From> for Either { - fn from(val: either::Either) -> Self { - match val { - either::Either::Left(l) => Either::Left(l), - either::Either::Right(r) => Either::Right(r), - } - } -} - -impl From> for either::Either { - fn from(val: Either) -> Self { - match val { - Either::Left(l) => either::Either::Left(l), - Either::Right(r) => either::Either::Right(r), - } - } -} - #[cfg(test)] impl Either { pub(self) fn unwrap_left(self) -> L { @@ -146,7 +129,7 @@ where L: Responder, R: Responder, { - type Body = body::EitherBody; + type Body = EitherBody; fn respond_to(self, req: &HttpRequest) -> HttpResponse { match self { @@ -165,7 +148,7 @@ pub enum EitherExtractError { /// Error from payload buffering, such as exceeding payload max size limit. Bytes(Error), - /// Error from primary extractor. + /// Error from primary and fallback extractors. Extract(L, R), } From dd4a372613339f6668dc248d2dbc414c3a6ccfad Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 14 Dec 2021 21:17:50 +0000 Subject: [PATCH 31/87] allow error handler middleware to return different body type (#2515) --- CHANGES.md | 3 + actix-router/src/url.rs | 53 ++++++++------ scripts/ci-test | 32 ++++++--- src/middleware/compat.rs | 22 +++--- src/middleware/condition.rs | 13 ++-- src/middleware/err_handlers.rs | 128 +++++++++++++++++++++------------ src/middleware/mod.rs | 4 +- 7 files changed, 162 insertions(+), 93 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b8d3ce8de..0c27aaa1c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,12 +7,15 @@ ### Changed * Align `DefaultHeader` method terminology, deprecating previous methods. [#2510] +* Response service types in `ErrorHandlers` middleware now use `ServiceResponse>` to allow changing the body type. [#2515] +* Both variants in `ErrorHandlerResponse` now use `ServiceResponse>`. [#2515] ### Removed * Top-level `EitherExtractError` export. [#2510] * Conversion implementations for `either` crate. [#2516] [#2510]: https://github.com/actix/actix-web/pull/2510 +[#2515]: https://github.com/actix/actix-web/pull/2515 [#2516]: https://github.com/actix/actix-web/pull/2516 diff --git a/actix-router/src/url.rs b/actix-router/src/url.rs index e08a7171a..10193dde8 100644 --- a/actix-router/src/url.rs +++ b/actix-router/src/url.rs @@ -2,22 +2,28 @@ use crate::ResourcePath; #[allow(dead_code)] const GEN_DELIMS: &[u8] = b":/?#[]@"; + #[allow(dead_code)] const SUB_DELIMS_WITHOUT_QS: &[u8] = b"!$'()*,"; + #[allow(dead_code)] const SUB_DELIMS: &[u8] = b"!$'()*,+?=;"; + #[allow(dead_code)] const RESERVED: &[u8] = b":/?#[]@!$'()*,+?=;"; + #[allow(dead_code)] const UNRESERVED: &[u8] = b"abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 -._~"; + const ALLOWED: &[u8] = b"abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 -._~ !$'()*,"; + const QS: &[u8] = b"+&=;b"; #[inline] @@ -34,19 +40,20 @@ thread_local! { static DEFAULT_QUOTER: Quoter = Quoter::new(b"@:", b"%/+"); } -#[derive(Default, Clone, Debug)] +#[derive(Debug, Clone, Default)] pub struct Url { uri: http::Uri, path: Option, } impl Url { + #[inline] pub fn new(uri: http::Uri) -> Url { let path = DEFAULT_QUOTER.with(|q| q.requote(uri.path().as_bytes())); - Url { uri, path } } + #[inline] pub fn with_quoter(uri: http::Uri, quoter: &Quoter) -> Url { Url { path: quoter.requote(uri.path().as_bytes()), @@ -54,15 +61,16 @@ impl Url { } } + #[inline] pub fn uri(&self) -> &http::Uri { &self.uri } + #[inline] pub fn path(&self) -> &str { - if let Some(ref s) = self.path { - s - } else { - self.uri.path() + match self.path { + Some(ref path) => path, + _ => self.uri.path(), } } @@ -86,6 +94,7 @@ impl ResourcePath for Url { } } +/// A quoter pub struct Quoter { safe_table: [u8; 16], protected_table: [u8; 16], @@ -93,7 +102,7 @@ pub struct Quoter { impl Quoter { pub fn new(safe: &[u8], protected: &[u8]) -> Quoter { - let mut q = Quoter { + let mut quoter = Quoter { safe_table: [0; 16], protected_table: [0; 16], }; @@ -101,24 +110,24 @@ impl Quoter { // prepare safe table for i in 0..128 { if ALLOWED.contains(&i) { - set_bit(&mut q.safe_table, i); + set_bit(&mut quoter.safe_table, i); } if QS.contains(&i) { - set_bit(&mut q.safe_table, i); + set_bit(&mut quoter.safe_table, i); } } for ch in safe { - set_bit(&mut q.safe_table, *ch) + set_bit(&mut quoter.safe_table, *ch) } // prepare protected table for ch in protected { - set_bit(&mut q.safe_table, *ch); - set_bit(&mut q.protected_table, *ch); + set_bit(&mut quoter.safe_table, *ch); + set_bit(&mut quoter.protected_table, *ch); } - q + quoter } pub fn requote(&self, val: &[u8]) -> Option { @@ -215,7 +224,7 @@ mod tests { } #[test] - fn test_parse_url() { + fn parse_url() { let re = "/user/{id}/test"; let path = match_url(re, "/user/2345/test"); @@ -231,24 +240,24 @@ mod tests { } #[test] - fn test_protected_chars() { + fn protected_chars() { let encoded = percent_encode(PROTECTED); let path = match_url("/user/{id}/test", format!("/user/{}/test", encoded)); assert_eq!(path.get("id").unwrap(), &encoded); } #[test] - fn test_non_protecteed_ascii() { - let nonprotected_ascii = ('\u{0}'..='\u{7F}') + fn non_protected_ascii() { + let non_protected_ascii = ('\u{0}'..='\u{7F}') .filter(|&c| c.is_ascii() && !PROTECTED.contains(&(c as u8))) .collect::(); - let encoded = percent_encode(nonprotected_ascii.as_bytes()); + let encoded = percent_encode(non_protected_ascii.as_bytes()); let path = match_url("/user/{id}/test", format!("/user/{}/test", encoded)); - assert_eq!(path.get("id").unwrap(), &nonprotected_ascii); + assert_eq!(path.get("id").unwrap(), &non_protected_ascii); } #[test] - fn test_valid_utf8_multibyte() { + fn valid_utf8_multibyte() { let test = ('\u{FF00}'..='\u{FFFF}').collect::(); let encoded = percent_encode(test.as_bytes()); let path = match_url("/a/{id}/b", format!("/a/{}/b", &encoded)); @@ -256,7 +265,7 @@ mod tests { } #[test] - fn test_invalid_utf8() { + fn invalid_utf8() { let invalid_utf8 = percent_encode((0x80..=0xff).collect::>().as_slice()); let uri = Uri::try_from(format!("/{}", invalid_utf8)).unwrap(); let path = Path::new(Url::new(uri)); @@ -266,7 +275,7 @@ mod tests { } #[test] - fn test_from_hex() { + fn hex_encoding() { let hex = b"0123456789abcdefABCDEF"; for i in 0..256 { diff --git a/scripts/ci-test b/scripts/ci-test index 98e13927d..3ab229665 100755 --- a/scripts/ci-test +++ b/scripts/ci-test @@ -4,15 +4,25 @@ set -x -cargo test --lib --tests -p=actix-router --all-features -cargo test --lib --tests -p=actix-http --all-features -cargo test --lib --tests -p=actix-web --features=rustls,openssl -- --skip=test_reading_deflate_encoding_large_random_rustls -cargo test --lib --tests -p=actix-web-codegen --all-features -cargo test --lib --tests -p=awc --all-features -cargo test --lib --tests -p=actix-http-test --all-features -cargo test --lib --tests -p=actix-test --all-features -cargo test --lib --tests -p=actix-files -cargo test --lib --tests -p=actix-multipart --all-features -cargo test --lib --tests -p=actix-web-actors --all-features +EXIT=0 -cargo test --workspace --doc +save_exit_code() { + eval $@ + local CMD_EXIT=$? + [ "$CMD_EXIT" = "0" ] || EXIT=$CMD_EXIT +} + +save_exit_code cargo test --lib --tests -p=actix-router --all-features +save_exit_code cargo test --lib --tests -p=actix-http --all-features +save_exit_code cargo test --lib --tests -p=actix-web --features=rustls,openssl -- --skip=test_reading_deflate_encoding_large_random_rustls +save_exit_code cargo test --lib --tests -p=actix-web-codegen --all-features +save_exit_code cargo test --lib --tests -p=awc --all-features +save_exit_code cargo test --lib --tests -p=actix-http-test --all-features +save_exit_code cargo test --lib --tests -p=actix-test --all-features +save_exit_code cargo test --lib --tests -p=actix-files +save_exit_code cargo test --lib --tests -p=actix-multipart --all-features +save_exit_code cargo test --lib --tests -p=actix-web-actors --all-features + +save_exit_code cargo test --workspace --doc + +exit $EXIT diff --git a/src/middleware/compat.rs b/src/middleware/compat.rs index ed441f7b9..d49c461c4 100644 --- a/src/middleware/compat.rs +++ b/src/middleware/compat.rs @@ -6,12 +6,15 @@ use std::{ task::{Context, Poll}, }; -use actix_http::body::MessageBody; -use actix_service::{Service, Transform}; use futures_core::{future::LocalBoxFuture, ready}; use pin_project_lite::pin_project; -use crate::{error::Error, service::ServiceResponse}; +use crate::{ + body::{BoxBody, MessageBody}, + dev::{Service, Transform}, + error::Error, + service::ServiceResponse, +}; /// Middleware for enabling any middleware to be used in [`Resource::wrap`](crate::Resource::wrap), /// [`Scope::wrap`](crate::Scope::wrap) and [`Condition`](super::Condition). @@ -52,7 +55,7 @@ where T::Response: MapServiceResponseBody, T::Error: Into, { - type Response = ServiceResponse; + type Response = ServiceResponse; type Error = Error; type Transform = CompatMiddleware; type InitError = T::InitError; @@ -77,7 +80,7 @@ where S::Response: MapServiceResponseBody, S::Error: Into, { - type Response = ServiceResponse; + type Response = ServiceResponse; type Error = Error; type Future = CompatMiddlewareFuture; @@ -102,7 +105,7 @@ where T: MapServiceResponseBody, E: Into, { - type Output = Result; + type Output = Result, Error>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let res = match ready!(self.project().fut.poll(cx)) { @@ -116,14 +119,15 @@ where /// Convert `ServiceResponse`'s `ResponseBody` generic type to `ResponseBody`. pub trait MapServiceResponseBody { - fn map_body(self) -> ServiceResponse; + fn map_body(self) -> ServiceResponse; } impl MapServiceResponseBody for ServiceResponse where - B: MessageBody + Unpin + 'static, + B: MessageBody + 'static, { - fn map_body(self) -> ServiceResponse { + #[inline] + fn map_body(self) -> ServiceResponse { self.map_into_boxed_body() } } diff --git a/src/middleware/condition.rs b/src/middleware/condition.rs index a7777a96b..659f88bc9 100644 --- a/src/middleware/condition.rs +++ b/src/middleware/condition.rs @@ -106,7 +106,7 @@ mod tests { header::{HeaderValue, CONTENT_TYPE}, StatusCode, }, - middleware::err_handlers::*, + middleware::{err_handlers::*, Compat}, test::{self, TestRequest}, HttpResponse, }; @@ -116,7 +116,8 @@ mod tests { res.response_mut() .headers_mut() .insert(CONTENT_TYPE, HeaderValue::from_static("0001")); - Ok(ErrorHandlerResponse::Response(res)) + + Ok(ErrorHandlerResponse::Response(res.map_into_left_body())) } #[actix_rt::test] @@ -125,7 +126,9 @@ mod tests { ok(req.into_response(HttpResponse::InternalServerError().finish())) }; - let mw = ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500); + let mw = Compat::new( + ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500), + ); let mw = Condition::new(true, mw) .new_transform(srv.into_service()) @@ -141,7 +144,9 @@ mod tests { ok(req.into_response(HttpResponse::InternalServerError().finish())) }; - let mw = ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500); + let mw = Compat::new( + ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500), + ); let mw = Condition::new(false, mw) .new_transform(srv.into_service()) diff --git a/src/middleware/err_handlers.rs b/src/middleware/err_handlers.rs index 756da30c3..fedefa6fa 100644 --- a/src/middleware/err_handlers.rs +++ b/src/middleware/err_handlers.rs @@ -13,6 +13,7 @@ use futures_core::{future::LocalBoxFuture, ready}; use pin_project_lite::pin_project; use crate::{ + body::EitherBody, dev::{ServiceRequest, ServiceResponse}, http::StatusCode, Error, Result, @@ -21,10 +22,10 @@ use crate::{ /// Return type for [`ErrorHandlers`] custom handlers. pub enum ErrorHandlerResponse { /// Immediate HTTP response. - Response(ServiceResponse), + Response(ServiceResponse>), /// A future that resolves to an HTTP response. - Future(LocalBoxFuture<'static, Result, Error>>), + Future(LocalBoxFuture<'static, Result>, Error>>), } type ErrorHandler = dyn Fn(ServiceResponse) -> Result>; @@ -44,7 +45,8 @@ type ErrorHandler = dyn Fn(ServiceResponse) -> Result = Rc>>>; impl Default for ErrorHandlers { fn default() -> Self { ErrorHandlers { - handlers: Rc::new(AHashMap::default()), + handlers: Default::default(), } } } @@ -95,7 +97,7 @@ where S::Future: 'static, B: 'static, { - type Response = ServiceResponse; + type Response = ServiceResponse>; type Error = Error; type Transform = ErrorHandlersMiddleware; type InitError = (); @@ -119,7 +121,7 @@ where S::Future: 'static, B: 'static, { - type Response = ServiceResponse; + type Response = ServiceResponse>; type Error = Error; type Future = ErrorHandlersFuture; @@ -143,8 +145,8 @@ pin_project! { fut: Fut, handlers: Handlers, }, - HandlerFuture { - fut: LocalBoxFuture<'static, Fut::Output>, + ErrorHandlerFuture { + fut: LocalBoxFuture<'static, Result>, Error>>, }, } } @@ -153,25 +155,29 @@ impl Future for ErrorHandlersFuture where Fut: Future, Error>>, { - type Output = Fut::Output; + type Output = Result>, Error>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.as_mut().project() { ErrorHandlersProj::ServiceFuture { fut, handlers } => { let res = ready!(fut.poll(cx))?; + match handlers.get(&res.status()) { Some(handler) => match handler(res)? { ErrorHandlerResponse::Response(res) => Poll::Ready(Ok(res)), ErrorHandlerResponse::Future(fut) => { self.as_mut() - .set(ErrorHandlersFuture::HandlerFuture { fut }); + .set(ErrorHandlersFuture::ErrorHandlerFuture { fut }); + self.poll(cx) } }, - None => Poll::Ready(Ok(res)), + + None => Poll::Ready(Ok(res.map_into_left_body())), } } - ErrorHandlersProj::HandlerFuture { fut } => fut.as_mut().poll(cx), + + ErrorHandlersProj::ErrorHandlerFuture { fut } => fut.as_mut().poll(cx), } } } @@ -180,32 +186,33 @@ where mod tests { use actix_service::IntoService; use actix_utils::future::ok; + use bytes::Bytes; use futures_util::future::FutureExt as _; use super::*; - use crate::http::{ - header::{HeaderValue, CONTENT_TYPE}, - StatusCode, + use crate::{ + http::{ + header::{HeaderValue, CONTENT_TYPE}, + StatusCode, + }, + test::{self, TestRequest}, }; - use crate::test::{self, TestRequest}; - use crate::HttpResponse; - - #[allow(clippy::unnecessary_wraps)] - fn render_500(mut res: ServiceResponse) -> Result> { - res.response_mut() - .headers_mut() - .insert(CONTENT_TYPE, HeaderValue::from_static("0001")); - Ok(ErrorHandlerResponse::Response(res)) - } #[actix_rt::test] - async fn test_handler() { - let srv = |req: ServiceRequest| { - ok(req.into_response(HttpResponse::InternalServerError().finish())) - }; + async fn add_header_error_handler() { + #[allow(clippy::unnecessary_wraps)] + fn error_handler(mut res: ServiceResponse) -> Result> { + res.response_mut() + .headers_mut() + .insert(CONTENT_TYPE, HeaderValue::from_static("0001")); + + Ok(ErrorHandlerResponse::Response(res.map_into_left_body())) + } + + let srv = test::default_service(StatusCode::INTERNAL_SERVER_ERROR); let mw = ErrorHandlers::new() - .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500) + .handler(StatusCode::INTERNAL_SERVER_ERROR, error_handler) .new_transform(srv.into_service()) .await .unwrap(); @@ -214,24 +221,25 @@ mod tests { assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); } - #[allow(clippy::unnecessary_wraps)] - fn render_500_async( - mut res: ServiceResponse, - ) -> Result> { - res.response_mut() - .headers_mut() - .insert(CONTENT_TYPE, HeaderValue::from_static("0001")); - Ok(ErrorHandlerResponse::Future(ok(res).boxed_local())) - } - #[actix_rt::test] - async fn test_handler_async() { - let srv = |req: ServiceRequest| { - ok(req.into_response(HttpResponse::InternalServerError().finish())) - }; + async fn add_header_error_handler_async() { + #[allow(clippy::unnecessary_wraps)] + fn error_handler( + mut res: ServiceResponse, + ) -> Result> { + res.response_mut() + .headers_mut() + .insert(CONTENT_TYPE, HeaderValue::from_static("0001")); + + Ok(ErrorHandlerResponse::Future( + ok(res.map_into_left_body()).boxed_local(), + )) + } + + let srv = test::default_service(StatusCode::INTERNAL_SERVER_ERROR); let mw = ErrorHandlers::new() - .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500_async) + .handler(StatusCode::INTERNAL_SERVER_ERROR, error_handler) .new_transform(srv.into_service()) .await .unwrap(); @@ -239,4 +247,34 @@ mod tests { let resp = test::call_service(&mw, TestRequest::default().to_srv_request()).await; assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); } + + #[actix_rt::test] + async fn changes_body_type() { + #[allow(clippy::unnecessary_wraps)] + fn error_handler( + res: ServiceResponse, + ) -> Result> { + let (req, res) = res.into_parts(); + let res = res.set_body(Bytes::from("sorry, that's no bueno")); + + let res = ServiceResponse::new(req, res) + .map_into_boxed_body() + .map_into_right_body(); + + Ok(ErrorHandlerResponse::Response(res)) + } + + let srv = test::default_service(StatusCode::INTERNAL_SERVER_ERROR); + + let mw = ErrorHandlers::new() + .handler(StatusCode::INTERNAL_SERVER_ERROR, error_handler) + .new_transform(srv.into_service()) + .await + .unwrap(); + + let res = test::call_service(&mw, TestRequest::default().to_srv_request()).await; + assert_eq!(test::read_body(res).await, "sorry, that's no bueno"); + } + + // TODO: test where error is thrown } diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 42d285580..0da9b9b2e 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -35,7 +35,7 @@ mod tests { .wrap(Condition::new(true, DefaultHeaders::new())) .wrap(DefaultHeaders::new().add(("X-Test2", "X-Value2"))) .wrap(ErrorHandlers::new().handler(StatusCode::FORBIDDEN, |res| { - Ok(ErrorHandlerResponse::Response(res)) + Ok(ErrorHandlerResponse::Response(res.map_into_left_body())) })) .wrap(Logger::default()) .wrap(NormalizePath::new(TrailingSlash::Trim)); @@ -44,7 +44,7 @@ mod tests { .wrap(NormalizePath::new(TrailingSlash::Trim)) .wrap(Logger::default()) .wrap(ErrorHandlers::new().handler(StatusCode::FORBIDDEN, |res| { - Ok(ErrorHandlerResponse::Response(res)) + Ok(ErrorHandlerResponse::Response(res.map_into_left_body())) })) .wrap(DefaultHeaders::new().add(("X-Test2", "X-Value2"))) .wrap(Condition::new(true, DefaultHeaders::new())) From 156cc20ac8af6455cb2438ba1b982265bac64521 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 15 Dec 2021 01:44:51 +0000 Subject: [PATCH 32/87] refactor testing utils (#2518) --- CHANGES.md | 6 + actix-http/src/test.rs | 2 +- actix-test/CHANGES.md | 4 + actix-test/src/lib.rs | 13 +- src/middleware/default_headers.rs | 7 +- src/middleware/err_handlers.rs | 6 +- src/test.rs | 909 ------------------------------ src/test/mod.rs | 81 +++ src/test/test_request.rs | 431 ++++++++++++++ src/test/test_services.rs | 31 + src/test/test_utils.rs | 474 ++++++++++++++++ src/types/either.rs | 2 - src/types/json.rs | 5 +- 13 files changed, 1043 insertions(+), 928 deletions(-) delete mode 100644 src/test.rs create mode 100644 src/test/mod.rs create mode 100644 src/test/test_request.rs create mode 100644 src/test/test_services.rs create mode 100644 src/test/test_utils.rs diff --git a/CHANGES.md b/CHANGES.md index 0c27aaa1c..6494ba4f6 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -9,14 +9,20 @@ * Align `DefaultHeader` method terminology, deprecating previous methods. [#2510] * Response service types in `ErrorHandlers` middleware now use `ServiceResponse>` to allow changing the body type. [#2515] * Both variants in `ErrorHandlerResponse` now use `ServiceResponse>`. [#2515] +* Rename `test::{default_service => simple_service}`. Old name is deprecated. [#2518] +* Rename `test::{read_response_json => call_and_read_body_json}`. Old name is deprecated. [#2518] +* Rename `test::{read_response => call_and_read_body}`. Old name is deprecated. [#2518] +* Relax body type and error bounds on test utilities. ### Removed * Top-level `EitherExtractError` export. [#2510] * Conversion implementations for `either` crate. [#2516] +* `test::load_stream` and `test::load_body`; replace usage with `body::to_bytes`. [#2518] [#2510]: https://github.com/actix/actix-web/pull/2510 [#2515]: https://github.com/actix/actix-web/pull/2515 [#2516]: https://github.com/actix/actix-web/pull/2516 +[#2518]: https://github.com/actix/actix-web/pull/2518 ## 4.0.0-beta.14 - 2021-12-11 diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs index 7e26ee865..ea80345fe 100644 --- a/actix-http/src/test.rs +++ b/actix-http/src/test.rs @@ -264,7 +264,7 @@ impl TestSeqBuffer { /// Create new empty `TestBuffer` instance. pub fn empty() -> Self { - Self::new("") + Self::new(BytesMut::new()) } pub fn read_buf(&self) -> Ref<'_, BytesMut> { diff --git a/actix-test/CHANGES.md b/actix-test/CHANGES.md index ec7d3e8d1..b7107b44f 100644 --- a/actix-test/CHANGES.md +++ b/actix-test/CHANGES.md @@ -1,6 +1,10 @@ # Changes ## Unreleased - 2021-xx-xx +* Re-export `actix_http::body::to_bytes`. [#2518] +* Update `actix_web::test` re-exports. [#2518] + +[#2518]: https://github.com/actix/actix-web/pull/2518 ## 0.1.0-beta.8 - 2021-12-11 diff --git a/actix-test/src/lib.rs b/actix-test/src/lib.rs index 934b8f3aa..3808ba69a 100644 --- a/actix-test/src/lib.rs +++ b/actix-test/src/lib.rs @@ -37,9 +37,14 @@ extern crate tls_rustls as rustls; use std::{fmt, net, thread, time::Duration}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; -pub use actix_http::test::TestBuffer; +pub use actix_http::{body::to_bytes, test::TestBuffer}; use actix_http::{header::HeaderMap, ws, HttpService, Method, Request, Response}; +pub use actix_http_test::unused_addr; use actix_service::{map_config, IntoServiceFactory, ServiceFactory, ServiceFactoryExt as _}; +pub use actix_web::test::{ + call_and_read_body, call_and_read_body_json, call_service, init_service, ok_service, + read_body, read_body_json, simple_service, TestRequest, +}; use actix_web::{ body::MessageBody, dev::{AppConfig, Server, ServerHandle, Service}, @@ -48,12 +53,6 @@ use actix_web::{ }; use awc::{error::PayloadError, Client, ClientRequest, ClientResponse, Connector}; use futures_core::Stream; - -pub use actix_http_test::unused_addr; -pub use actix_web::test::{ - call_service, default_service, init_service, load_stream, ok_service, read_body, - read_body_json, read_response, read_response_json, TestRequest, -}; use tokio::sync::mpsc; /// Start default [`TestServer`]. diff --git a/src/middleware/default_headers.rs b/src/middleware/default_headers.rs index 257467710..89210b156 100644 --- a/src/middleware/default_headers.rs +++ b/src/middleware/default_headers.rs @@ -194,7 +194,7 @@ mod tests { use crate::{ dev::ServiceRequest, http::header::CONTENT_TYPE, - test::{ok_service, TestRequest}, + test::{self, TestRequest}, HttpResponse, }; @@ -203,7 +203,7 @@ mod tests { let mw = DefaultHeaders::new() .add(("X-TEST", "0001")) .add(("X-TEST-TWO", HeaderValue::from_static("123"))) - .new_transform(ok_service()) + .new_transform(test::ok_service()) .await .unwrap(); @@ -234,10 +234,9 @@ mod tests { #[actix_rt::test] async fn adding_content_type() { - let srv = |req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())); let mw = DefaultHeaders::new() .add_content_type() - .new_transform(srv.into_service()) + .new_transform(test::ok_service()) .await .unwrap(); diff --git a/src/middleware/err_handlers.rs b/src/middleware/err_handlers.rs index fedefa6fa..6d064372f 100644 --- a/src/middleware/err_handlers.rs +++ b/src/middleware/err_handlers.rs @@ -209,7 +209,7 @@ mod tests { Ok(ErrorHandlerResponse::Response(res.map_into_left_body())) } - let srv = test::default_service(StatusCode::INTERNAL_SERVER_ERROR); + let srv = test::simple_service(StatusCode::INTERNAL_SERVER_ERROR); let mw = ErrorHandlers::new() .handler(StatusCode::INTERNAL_SERVER_ERROR, error_handler) @@ -236,7 +236,7 @@ mod tests { )) } - let srv = test::default_service(StatusCode::INTERNAL_SERVER_ERROR); + let srv = test::simple_service(StatusCode::INTERNAL_SERVER_ERROR); let mw = ErrorHandlers::new() .handler(StatusCode::INTERNAL_SERVER_ERROR, error_handler) @@ -264,7 +264,7 @@ mod tests { Ok(ErrorHandlerResponse::Response(res)) } - let srv = test::default_service(StatusCode::INTERNAL_SERVER_ERROR); + let srv = test::simple_service(StatusCode::INTERNAL_SERVER_ERROR); let mw = ErrorHandlers::new() .handler(StatusCode::INTERNAL_SERVER_ERROR, error_handler) diff --git a/src/test.rs b/src/test.rs deleted file mode 100644 index 5ef2343a8..000000000 --- a/src/test.rs +++ /dev/null @@ -1,909 +0,0 @@ -//! Various helpers for Actix applications to use during testing. - -use std::{borrow::Cow, net::SocketAddr, rc::Rc}; - -pub use actix_http::test::TestBuffer; -use actix_http::{ - header::TryIntoHeaderPair, test::TestRequest as HttpTestRequest, Extensions, Method, - Request, StatusCode, Uri, Version, -}; -use actix_router::{Path, ResourceDef, Url}; -use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory}; -use actix_utils::future::{ok, poll_fn}; -use futures_core::Stream; -use futures_util::StreamExt as _; -use serde::{de::DeserializeOwned, Serialize}; - -#[cfg(feature = "cookies")] -use crate::cookie::{Cookie, CookieJar}; -use crate::{ - app_service::AppInitServiceState, - body::{self, BoxBody, MessageBody}, - config::AppConfig, - data::Data, - dev::Payload, - http::header::ContentType, - rmap::ResourceMap, - service::{ServiceRequest, ServiceResponse}, - web::{Bytes, BytesMut}, - Error, HttpRequest, HttpResponse, HttpResponseBuilder, -}; - -/// Create service that always responds with `HttpResponse::Ok()` and no body. -pub fn ok_service( -) -> impl Service, Error = Error> { - default_service(StatusCode::OK) -} - -/// Create service that always responds with given status code and no body. -pub fn default_service( - status_code: StatusCode, -) -> impl Service, Error = Error> { - (move |req: ServiceRequest| { - ok(req.into_response(HttpResponseBuilder::new(status_code).finish())) - }) - .into_service() -} - -/// Initialize service from application builder instance. -/// -/// ``` -/// use actix_service::Service; -/// use actix_web::{test, web, App, HttpResponse, http::StatusCode}; -/// -/// #[actix_web::test] -/// async fn test_init_service() { -/// let app = test::init_service( -/// App::new() -/// .service(web::resource("/test").to(|| async { "OK" })) -/// ).await; -/// -/// // Create request object -/// let req = test::TestRequest::with_uri("/test").to_request(); -/// -/// // Execute application -/// let resp = app.call(req).await.unwrap(); -/// assert_eq!(resp.status(), StatusCode::OK); -/// } -/// ``` -pub async fn init_service( - app: R, -) -> impl Service, Error = E> -where - R: IntoServiceFactory, - S: ServiceFactory, Error = E>, - S::InitError: std::fmt::Debug, -{ - try_init_service(app) - .await - .expect("service initialization failed") -} - -/// Fallible version of [`init_service`] that allows testing initialization errors. -pub(crate) async fn try_init_service( - app: R, -) -> Result, Error = E>, S::InitError> -where - R: IntoServiceFactory, - S: ServiceFactory, Error = E>, - S::InitError: std::fmt::Debug, -{ - let srv = app.into_factory(); - srv.new_service(AppConfig::default()).await -} - -/// Calls service and waits for response future completion. -/// -/// ``` -/// use actix_web::{test, web, App, HttpResponse, http::StatusCode}; -/// -/// #[actix_web::test] -/// async fn test_response() { -/// let app = test::init_service( -/// App::new() -/// .service(web::resource("/test").to(|| async { -/// HttpResponse::Ok() -/// })) -/// ).await; -/// -/// // Create request object -/// let req = test::TestRequest::with_uri("/test").to_request(); -/// -/// // Call application -/// let resp = test::call_service(&app, req).await; -/// assert_eq!(resp.status(), StatusCode::OK); -/// } -/// ``` -pub async fn call_service(app: &S, req: R) -> S::Response -where - S: Service, Error = E>, - E: std::fmt::Debug, -{ - app.call(req).await.unwrap() -} - -/// Helper function that returns a response body of a TestRequest -/// -/// ``` -/// use actix_web::{test, web, App, HttpResponse, http::header}; -/// use bytes::Bytes; -/// -/// #[actix_web::test] -/// async fn test_index() { -/// let app = test::init_service( -/// App::new().service( -/// web::resource("/index.html") -/// .route(web::post().to(|| async { -/// HttpResponse::Ok().body("welcome!") -/// }))) -/// ).await; -/// -/// let req = test::TestRequest::post() -/// .uri("/index.html") -/// .header(header::CONTENT_TYPE, "application/json") -/// .to_request(); -/// -/// let result = test::read_response(&app, req).await; -/// assert_eq!(result, Bytes::from_static(b"welcome!")); -/// } -/// ``` -pub async fn read_response(app: &S, req: Request) -> Bytes -where - S: Service, Error = Error>, - B: MessageBody + Unpin, - B::Error: Into, -{ - let resp = app - .call(req) - .await - .unwrap_or_else(|e| panic!("read_response failed at application call: {}", e)); - - let body = resp.into_body(); - let mut bytes = BytesMut::new(); - - actix_rt::pin!(body); - while let Some(item) = poll_fn(|cx| body.as_mut().poll_next(cx)).await { - bytes.extend_from_slice(&item.map_err(Into::into).unwrap()); - } - - bytes.freeze() -} - -/// Helper function that returns a response body of a ServiceResponse. -/// -/// ``` -/// use actix_web::{test, web, App, HttpResponse, http::header}; -/// use bytes::Bytes; -/// -/// #[actix_web::test] -/// async fn test_index() { -/// let app = test::init_service( -/// App::new().service( -/// web::resource("/index.html") -/// .route(web::post().to(|| async { -/// HttpResponse::Ok().body("welcome!") -/// }))) -/// ).await; -/// -/// let req = test::TestRequest::post() -/// .uri("/index.html") -/// .header(header::CONTENT_TYPE, "application/json") -/// .to_request(); -/// -/// let resp = test::call_service(&app, req).await; -/// let result = test::read_body(resp).await; -/// assert_eq!(result, Bytes::from_static(b"welcome!")); -/// } -/// ``` -pub async fn read_body(res: ServiceResponse) -> Bytes -where - B: MessageBody + Unpin, - B::Error: Into, -{ - let body = res.into_body(); - let mut bytes = BytesMut::new(); - - actix_rt::pin!(body); - while let Some(item) = poll_fn(|cx| body.as_mut().poll_next(cx)).await { - bytes.extend_from_slice(&item.map_err(Into::into).unwrap()); - } - - bytes.freeze() -} - -/// Helper function that returns a deserialized response body of a ServiceResponse. -/// -/// ``` -/// use actix_web::{App, test, web, HttpResponse, http::header}; -/// use serde::{Serialize, Deserialize}; -/// -/// #[derive(Serialize, Deserialize)] -/// pub struct Person { -/// id: String, -/// name: String, -/// } -/// -/// #[actix_web::test] -/// async fn test_post_person() { -/// let app = test::init_service( -/// App::new().service( -/// web::resource("/people") -/// .route(web::post().to(|person: web::Json| async { -/// HttpResponse::Ok() -/// .json(person)}) -/// )) -/// ).await; -/// -/// let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); -/// -/// let resp = test::TestRequest::post() -/// .uri("/people") -/// .header(header::CONTENT_TYPE, "application/json") -/// .set_payload(payload) -/// .send_request(&mut app) -/// .await; -/// -/// assert!(resp.status().is_success()); -/// -/// let result: Person = test::read_body_json(resp).await; -/// } -/// ``` -pub async fn read_body_json(res: ServiceResponse) -> T -where - B: MessageBody + Unpin, - B::Error: Into, - T: DeserializeOwned, -{ - let body = read_body(res).await; - - serde_json::from_slice(&body).unwrap_or_else(|e| { - panic!( - "read_response_json failed during deserialization of body: {:?}, {}", - body, e - ) - }) -} - -pub async fn load_stream(mut stream: S) -> Result -where - S: Stream> + Unpin, -{ - let mut data = BytesMut::new(); - while let Some(item) = stream.next().await { - data.extend_from_slice(&item?); - } - Ok(data.freeze()) -} - -pub async fn load_body(body: B) -> Result -where - B: MessageBody + Unpin, - B::Error: Into, -{ - body::to_bytes(body).await.map_err(Into::into) -} - -/// Helper function that returns a deserialized response body of a TestRequest -/// -/// ``` -/// use actix_web::{App, test, web, HttpResponse, http::header}; -/// use serde::{Serialize, Deserialize}; -/// -/// #[derive(Serialize, Deserialize)] -/// pub struct Person { -/// id: String, -/// name: String -/// } -/// -/// #[actix_web::test] -/// async fn test_add_person() { -/// let app = test::init_service( -/// App::new().service( -/// web::resource("/people") -/// .route(web::post().to(|person: web::Json| async { -/// HttpResponse::Ok() -/// .json(person)}) -/// )) -/// ).await; -/// -/// let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); -/// -/// let req = test::TestRequest::post() -/// .uri("/people") -/// .header(header::CONTENT_TYPE, "application/json") -/// .set_payload(payload) -/// .to_request(); -/// -/// let result: Person = test::read_response_json(&mut app, req).await; -/// } -/// ``` -pub async fn read_response_json(app: &S, req: Request) -> T -where - S: Service, Error = Error>, - B: MessageBody + Unpin, - B::Error: Into, - T: DeserializeOwned, -{ - let body = read_response(app, req).await; - - serde_json::from_slice(&body).unwrap_or_else(|_| { - panic!( - "read_response_json failed during deserialization of body: {:?}", - body - ) - }) -} - -/// Test `Request` builder. -/// -/// For unit testing, actix provides a request builder type and a simple handler runner. TestRequest implements a builder-like pattern. -/// You can generate various types of request via TestRequest's methods: -/// * `TestRequest::to_request` creates `actix_http::Request` instance. -/// * `TestRequest::to_srv_request` creates `ServiceRequest` instance, which is used for testing middlewares and chain adapters. -/// * `TestRequest::to_srv_response` creates `ServiceResponse` instance. -/// * `TestRequest::to_http_request` creates `HttpRequest` instance, which is used for testing handlers. -/// -/// ``` -/// use actix_web::{test, HttpRequest, HttpResponse, HttpMessage}; -/// use actix_web::http::{header, StatusCode}; -/// -/// async fn index(req: HttpRequest) -> HttpResponse { -/// if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) { -/// HttpResponse::Ok().into() -/// } else { -/// HttpResponse::BadRequest().into() -/// } -/// } -/// -/// #[actix_web::test] -/// async fn test_index() { -/// let req = test::TestRequest::default().insert_header("content-type", "text/plain") -/// .to_http_request(); -/// -/// let resp = index(req).await.unwrap(); -/// assert_eq!(resp.status(), StatusCode::OK); -/// -/// let req = test::TestRequest::default().to_http_request(); -/// let resp = index(req).await.unwrap(); -/// assert_eq!(resp.status(), StatusCode::BAD_REQUEST); -/// } -/// ``` -pub struct TestRequest { - req: HttpTestRequest, - rmap: ResourceMap, - config: AppConfig, - path: Path, - peer_addr: Option, - app_data: Extensions, - #[cfg(feature = "cookies")] - cookies: CookieJar, -} - -impl Default for TestRequest { - fn default() -> TestRequest { - TestRequest { - req: HttpTestRequest::default(), - rmap: ResourceMap::new(ResourceDef::new("")), - config: AppConfig::default(), - path: Path::new(Url::new(Uri::default())), - peer_addr: None, - app_data: Extensions::new(), - #[cfg(feature = "cookies")] - cookies: CookieJar::new(), - } - } -} - -#[allow(clippy::wrong_self_convention)] -impl TestRequest { - /// Create TestRequest and set request uri - pub fn with_uri(path: &str) -> TestRequest { - TestRequest::default().uri(path) - } - - /// Create TestRequest and set method to `Method::GET` - pub fn get() -> TestRequest { - TestRequest::default().method(Method::GET) - } - - /// Create TestRequest and set method to `Method::POST` - pub fn post() -> TestRequest { - TestRequest::default().method(Method::POST) - } - - /// Create TestRequest and set method to `Method::PUT` - pub fn put() -> TestRequest { - TestRequest::default().method(Method::PUT) - } - - /// Create TestRequest and set method to `Method::PATCH` - pub fn patch() -> TestRequest { - TestRequest::default().method(Method::PATCH) - } - - /// Create TestRequest and set method to `Method::DELETE` - pub fn delete() -> TestRequest { - TestRequest::default().method(Method::DELETE) - } - - /// Set HTTP version of this request - pub fn version(mut self, ver: Version) -> Self { - self.req.version(ver); - self - } - - /// Set HTTP method of this request - pub fn method(mut self, meth: Method) -> Self { - self.req.method(meth); - self - } - - /// Set HTTP Uri of this request - pub fn uri(mut self, path: &str) -> Self { - self.req.uri(path); - self - } - - /// Insert a header, replacing any that were set with an equivalent field name. - pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self { - self.req.insert_header(header); - self - } - - /// Append a header, keeping any that were set with an equivalent field name. - pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self { - self.req.append_header(header); - self - } - - /// Set cookie for this request. - #[cfg(feature = "cookies")] - pub fn cookie(mut self, cookie: Cookie<'_>) -> Self { - self.cookies.add(cookie.into_owned()); - self - } - - /// Set request path pattern parameter. - /// - /// # Examples - /// ``` - /// use actix_web::test::TestRequest; - /// - /// let req = TestRequest::default().param("foo", "bar"); - /// let req = TestRequest::default().param("foo".to_owned(), "bar".to_owned()); - /// ``` - pub fn param( - mut self, - name: impl Into>, - value: impl Into>, - ) -> Self { - self.path.add_static(name, value); - self - } - - /// Set peer addr. - pub fn peer_addr(mut self, addr: SocketAddr) -> Self { - self.peer_addr = Some(addr); - self - } - - /// Set request payload. - pub fn set_payload>(mut self, data: B) -> Self { - self.req.set_payload(data); - self - } - - /// Serialize `data` to a URL encoded form and set it as the request payload. The `Content-Type` - /// header is set to `application/x-www-form-urlencoded`. - pub fn set_form(mut self, data: &T) -> Self { - let bytes = serde_urlencoded::to_string(data) - .expect("Failed to serialize test data as a urlencoded form"); - self.req.set_payload(bytes); - self.req.insert_header(ContentType::form_url_encoded()); - self - } - - /// Serialize `data` to JSON and set it as the request payload. The `Content-Type` header is - /// set to `application/json`. - pub fn set_json(mut self, data: &T) -> Self { - let bytes = serde_json::to_string(data).expect("Failed to serialize test data to json"); - self.req.set_payload(bytes); - self.req.insert_header(ContentType::json()); - self - } - - /// Set application data. This is equivalent of `App::data()` method - /// for testing purpose. - pub fn data(mut self, data: T) -> Self { - self.app_data.insert(Data::new(data)); - self - } - - /// Set application data. This is equivalent of `App::app_data()` method - /// for testing purpose. - pub fn app_data(mut self, data: T) -> Self { - self.app_data.insert(data); - self - } - - #[cfg(test)] - /// Set request config - pub(crate) fn rmap(mut self, rmap: ResourceMap) -> Self { - self.rmap = rmap; - self - } - - fn finish(&mut self) -> Request { - // mut used when cookie feature is enabled - #[allow(unused_mut)] - let mut req = self.req.finish(); - - #[cfg(feature = "cookies")] - { - use actix_http::header::{HeaderValue, COOKIE}; - - let cookie: String = self - .cookies - .delta() - // ensure only name=value is written to cookie header - .map(|c| c.stripped().encoded().to_string()) - .collect::>() - .join("; "); - - if !cookie.is_empty() { - req.headers_mut() - .insert(COOKIE, HeaderValue::from_str(&cookie).unwrap()); - } - } - - req - } - - /// Complete request creation and generate `Request` instance - pub fn to_request(mut self) -> Request { - let mut req = self.finish(); - req.head_mut().peer_addr = self.peer_addr; - req - } - - /// Complete request creation and generate `ServiceRequest` instance - pub fn to_srv_request(mut self) -> ServiceRequest { - let (mut head, payload) = self.finish().into_parts(); - head.peer_addr = self.peer_addr; - self.path.get_mut().update(&head.uri); - - let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone()); - - ServiceRequest::new( - HttpRequest::new( - self.path, - head, - app_state, - Rc::new(self.app_data), - None, - Default::default(), - ), - payload, - ) - } - - /// Complete request creation and generate `ServiceResponse` instance - pub fn to_srv_response(self, res: HttpResponse) -> ServiceResponse { - self.to_srv_request().into_response(res) - } - - /// Complete request creation and generate `HttpRequest` instance - pub fn to_http_request(mut self) -> HttpRequest { - let (mut head, _) = self.finish().into_parts(); - head.peer_addr = self.peer_addr; - self.path.get_mut().update(&head.uri); - - let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone()); - - HttpRequest::new( - self.path, - head, - app_state, - Rc::new(self.app_data), - None, - Default::default(), - ) - } - - /// Complete request creation and generate `HttpRequest` and `Payload` instances - pub fn to_http_parts(mut self) -> (HttpRequest, Payload) { - let (mut head, payload) = self.finish().into_parts(); - head.peer_addr = self.peer_addr; - self.path.get_mut().update(&head.uri); - - let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone()); - - let req = HttpRequest::new( - self.path, - head, - app_state, - Rc::new(self.app_data), - None, - Default::default(), - ); - - (req, payload) - } - - /// Complete request creation, calls service and waits for response future completion. - pub async fn send_request(self, app: &S) -> S::Response - where - S: Service, Error = E>, - E: std::fmt::Debug, - { - let req = self.to_request(); - call_service(app, req).await - } - - #[cfg(test)] - pub fn set_server_hostname(&mut self, host: &str) { - self.config.set_host(host) - } -} - -/// Reduces boilerplate code when testing expected response payloads. -#[cfg(test)] -macro_rules! assert_body_eq { - ($res:ident, $expected:expr) => { - assert_eq!( - ::actix_http::body::to_bytes($res.into_body()) - .await - .expect("body read should have succeeded"), - Bytes::from_static($expected), - ) - }; -} - -#[cfg(test)] -pub(crate) use assert_body_eq; - -#[cfg(test)] -mod tests { - use std::time::SystemTime; - - use actix_http::HttpMessage; - use serde::{Deserialize, Serialize}; - - use super::*; - use crate::{http::header, web, App, HttpResponse, Responder}; - - #[actix_rt::test] - async fn test_basics() { - let req = TestRequest::default() - .version(Version::HTTP_2) - .insert_header(header::ContentType::json()) - .insert_header(header::Date(SystemTime::now().into())) - .param("test", "123") - .data(10u32) - .app_data(20u64) - .peer_addr("127.0.0.1:8081".parse().unwrap()) - .to_http_request(); - assert!(req.headers().contains_key(header::CONTENT_TYPE)); - assert!(req.headers().contains_key(header::DATE)); - assert_eq!( - req.head().peer_addr, - Some("127.0.0.1:8081".parse().unwrap()) - ); - assert_eq!(&req.match_info()["test"], "123"); - assert_eq!(req.version(), Version::HTTP_2); - let data = req.app_data::>().unwrap(); - assert!(req.app_data::>().is_none()); - assert_eq!(*data.get_ref(), 10); - - assert!(req.app_data::().is_none()); - let data = req.app_data::().unwrap(); - assert_eq!(*data, 20); - } - - #[actix_rt::test] - async fn test_request_methods() { - let app = init_service( - App::new().service( - web::resource("/index.html") - .route(web::put().to(|| HttpResponse::Ok().body("put!"))) - .route(web::patch().to(|| HttpResponse::Ok().body("patch!"))) - .route(web::delete().to(|| HttpResponse::Ok().body("delete!"))), - ), - ) - .await; - - let put_req = TestRequest::put() - .uri("/index.html") - .insert_header((header::CONTENT_TYPE, "application/json")) - .to_request(); - - let result = read_response(&app, put_req).await; - assert_eq!(result, Bytes::from_static(b"put!")); - - let patch_req = TestRequest::patch() - .uri("/index.html") - .insert_header((header::CONTENT_TYPE, "application/json")) - .to_request(); - - let result = read_response(&app, patch_req).await; - assert_eq!(result, Bytes::from_static(b"patch!")); - - let delete_req = TestRequest::delete().uri("/index.html").to_request(); - let result = read_response(&app, delete_req).await; - assert_eq!(result, Bytes::from_static(b"delete!")); - } - - #[actix_rt::test] - async fn test_response() { - let app = init_service( - App::new().service( - web::resource("/index.html") - .route(web::post().to(|| HttpResponse::Ok().body("welcome!"))), - ), - ) - .await; - - let req = TestRequest::post() - .uri("/index.html") - .insert_header((header::CONTENT_TYPE, "application/json")) - .to_request(); - - let result = read_response(&app, req).await; - assert_eq!(result, Bytes::from_static(b"welcome!")); - } - - #[actix_rt::test] - async fn test_send_request() { - let app = init_service( - App::new().service( - web::resource("/index.html") - .route(web::get().to(|| HttpResponse::Ok().body("welcome!"))), - ), - ) - .await; - - let resp = TestRequest::get() - .uri("/index.html") - .send_request(&app) - .await; - - let result = read_body(resp).await; - assert_eq!(result, Bytes::from_static(b"welcome!")); - } - - #[derive(Serialize, Deserialize)] - pub struct Person { - id: String, - name: String, - } - - #[actix_rt::test] - async fn test_response_json() { - let app = init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Json| HttpResponse::Ok().json(person)), - ))) - .await; - - let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); - - let req = TestRequest::post() - .uri("/people") - .insert_header((header::CONTENT_TYPE, "application/json")) - .set_payload(payload) - .to_request(); - - let result: Person = read_response_json(&app, req).await; - assert_eq!(&result.id, "12345"); - } - - #[actix_rt::test] - async fn test_body_json() { - let app = init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Json| HttpResponse::Ok().json(person)), - ))) - .await; - - let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); - - let resp = TestRequest::post() - .uri("/people") - .insert_header((header::CONTENT_TYPE, "application/json")) - .set_payload(payload) - .send_request(&app) - .await; - - let result: Person = read_body_json(resp).await; - assert_eq!(&result.name, "User name"); - } - - #[actix_rt::test] - async fn test_request_response_form() { - let app = init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Form| HttpResponse::Ok().json(person)), - ))) - .await; - - let payload = Person { - id: "12345".to_string(), - name: "User name".to_string(), - }; - - let req = TestRequest::post() - .uri("/people") - .set_form(&payload) - .to_request(); - - assert_eq!(req.content_type(), "application/x-www-form-urlencoded"); - - let result: Person = read_response_json(&app, req).await; - assert_eq!(&result.id, "12345"); - assert_eq!(&result.name, "User name"); - } - - #[actix_rt::test] - async fn test_request_response_json() { - let app = init_service(App::new().service(web::resource("/people").route( - web::post().to(|person: web::Json| HttpResponse::Ok().json(person)), - ))) - .await; - - let payload = Person { - id: "12345".to_string(), - name: "User name".to_string(), - }; - - let req = TestRequest::post() - .uri("/people") - .set_json(&payload) - .to_request(); - - assert_eq!(req.content_type(), "application/json"); - - let result: Person = read_response_json(&app, req).await; - assert_eq!(&result.id, "12345"); - assert_eq!(&result.name, "User name"); - } - - #[actix_rt::test] - async fn test_async_with_block() { - async fn async_with_block() -> Result { - let res = web::block(move || Some(4usize).ok_or("wrong")).await; - - match res { - Ok(value) => Ok(HttpResponse::Ok() - .content_type("text/plain") - .body(format!("Async with block value: {:?}", value))), - Err(_) => panic!("Unexpected"), - } - } - - let app = - init_service(App::new().service(web::resource("/index.html").to(async_with_block))) - .await; - - let req = TestRequest::post().uri("/index.html").to_request(); - let res = app.call(req).await.unwrap(); - assert!(res.status().is_success()); - } - - // allow deprecated App::data - #[allow(deprecated)] - #[actix_rt::test] - async fn test_server_data() { - async fn handler(data: web::Data) -> impl Responder { - assert_eq!(**data, 10); - HttpResponse::Ok() - } - - let app = init_service( - App::new() - .data(10usize) - .service(web::resource("/index.html").to(handler)), - ) - .await; - - let req = TestRequest::post().uri("/index.html").to_request(); - let res = app.call(req).await.unwrap(); - assert!(res.status().is_success()); - } -} diff --git a/src/test/mod.rs b/src/test/mod.rs new file mode 100644 index 000000000..a29dfc437 --- /dev/null +++ b/src/test/mod.rs @@ -0,0 +1,81 @@ +//! Various helpers for Actix applications to use during testing. +//! +//! # Creating A Test Service +//! - [`init_service`] +//! +//! # Off-The-Shelf Test Services +//! - [`ok_service`] +//! - [`simple_service`] +//! +//! # Calling Test Service +//! - [`TestRequest`] +//! - [`call_service`] +//! - [`call_and_read_body`] +//! - [`call_and_read_body_json`] +//! +//! # Reading Response Payloads +//! - [`read_body`] +//! - [`read_body_json`] + +// TODO: more docs on generally how testing works with these parts + +pub use actix_http::test::TestBuffer; + +mod test_request; +mod test_services; +mod test_utils; + +pub use self::test_request::TestRequest; +#[allow(deprecated)] +pub use self::test_services::{default_service, ok_service, simple_service}; +#[allow(deprecated)] +pub use self::test_utils::{ + call_and_read_body, call_and_read_body_json, call_service, init_service, read_body, + read_body_json, read_response, read_response_json, +}; + +#[cfg(test)] +pub(crate) use self::test_utils::try_init_service; + +/// Reduces boilerplate code when testing expected response payloads. +/// +/// Must be used inside an async test. Works for both `ServiceRequest` and `HttpRequest`. +/// +/// # Examples +/// ``` +/// use actix_web::{http::StatusCode, HttpResponse}; +/// +/// let res = HttpResponse::with_body(StatusCode::OK, "http response"); +/// assert_body_eq!(res, b"http response"); +/// ``` +#[cfg(test)] +macro_rules! assert_body_eq { + ($res:ident, $expected:expr) => { + assert_eq!( + ::actix_http::body::to_bytes($res.into_body()) + .await + .expect("error reading test response body"), + ::bytes::Bytes::from_static($expected), + ) + }; +} + +#[cfg(test)] +pub(crate) use assert_body_eq; + +#[cfg(test)] +mod tests { + use super::*; + use crate::{http::StatusCode, service::ServiceResponse, HttpResponse}; + + #[actix_rt::test] + async fn assert_body_works_for_service_and_regular_response() { + let res = HttpResponse::with_body(StatusCode::OK, "http response"); + assert_body_eq!(res, b"http response"); + + let req = TestRequest::default().to_http_request(); + let res = HttpResponse::with_body(StatusCode::OK, "service response"); + let res = ServiceResponse::new(req, res); + assert_body_eq!(res, b"service response"); + } +} diff --git a/src/test/test_request.rs b/src/test/test_request.rs new file mode 100644 index 000000000..fd3355ef3 --- /dev/null +++ b/src/test/test_request.rs @@ -0,0 +1,431 @@ +use std::{borrow::Cow, net::SocketAddr, rc::Rc}; + +use actix_http::{test::TestRequest as HttpTestRequest, Request}; +use serde::Serialize; + +use crate::{ + app_service::AppInitServiceState, + config::AppConfig, + data::Data, + dev::{Extensions, Path, Payload, ResourceDef, Service, Url}, + http::header::ContentType, + http::{header::TryIntoHeaderPair, Method, Uri, Version}, + rmap::ResourceMap, + service::{ServiceRequest, ServiceResponse}, + test, + web::Bytes, + HttpRequest, HttpResponse, +}; + +#[cfg(feature = "cookies")] +use crate::cookie::{Cookie, CookieJar}; + +/// Test `Request` builder. +/// +/// For unit testing, actix provides a request builder type and a simple handler runner. TestRequest implements a builder-like pattern. +/// You can generate various types of request via TestRequest's methods: +/// * `TestRequest::to_request` creates `actix_http::Request` instance. +/// * `TestRequest::to_srv_request` creates `ServiceRequest` instance, which is used for testing middlewares and chain adapters. +/// * `TestRequest::to_srv_response` creates `ServiceResponse` instance. +/// * `TestRequest::to_http_request` creates `HttpRequest` instance, which is used for testing handlers. +/// +/// ``` +/// use actix_web::{test, HttpRequest, HttpResponse, HttpMessage}; +/// use actix_web::http::{header, StatusCode}; +/// +/// async fn index(req: HttpRequest) -> HttpResponse { +/// if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) { +/// HttpResponse::Ok().into() +/// } else { +/// HttpResponse::BadRequest().into() +/// } +/// } +/// +/// #[actix_web::test] +/// async fn test_index() { +/// let req = test::TestRequest::default().insert_header("content-type", "text/plain") +/// .to_http_request(); +/// +/// let resp = index(req).await.unwrap(); +/// assert_eq!(resp.status(), StatusCode::OK); +/// +/// let req = test::TestRequest::default().to_http_request(); +/// let resp = index(req).await.unwrap(); +/// assert_eq!(resp.status(), StatusCode::BAD_REQUEST); +/// } +/// ``` +pub struct TestRequest { + req: HttpTestRequest, + rmap: ResourceMap, + config: AppConfig, + path: Path, + peer_addr: Option, + app_data: Extensions, + #[cfg(feature = "cookies")] + cookies: CookieJar, +} + +impl Default for TestRequest { + fn default() -> TestRequest { + TestRequest { + req: HttpTestRequest::default(), + rmap: ResourceMap::new(ResourceDef::new("")), + config: AppConfig::default(), + path: Path::new(Url::new(Uri::default())), + peer_addr: None, + app_data: Extensions::new(), + #[cfg(feature = "cookies")] + cookies: CookieJar::new(), + } + } +} + +#[allow(clippy::wrong_self_convention)] +impl TestRequest { + /// Create TestRequest and set request uri + pub fn with_uri(path: &str) -> TestRequest { + TestRequest::default().uri(path) + } + + /// Create TestRequest and set method to `Method::GET` + pub fn get() -> TestRequest { + TestRequest::default().method(Method::GET) + } + + /// Create TestRequest and set method to `Method::POST` + pub fn post() -> TestRequest { + TestRequest::default().method(Method::POST) + } + + /// Create TestRequest and set method to `Method::PUT` + pub fn put() -> TestRequest { + TestRequest::default().method(Method::PUT) + } + + /// Create TestRequest and set method to `Method::PATCH` + pub fn patch() -> TestRequest { + TestRequest::default().method(Method::PATCH) + } + + /// Create TestRequest and set method to `Method::DELETE` + pub fn delete() -> TestRequest { + TestRequest::default().method(Method::DELETE) + } + + /// Set HTTP version of this request + pub fn version(mut self, ver: Version) -> Self { + self.req.version(ver); + self + } + + /// Set HTTP method of this request + pub fn method(mut self, meth: Method) -> Self { + self.req.method(meth); + self + } + + /// Set HTTP Uri of this request + pub fn uri(mut self, path: &str) -> Self { + self.req.uri(path); + self + } + + /// Insert a header, replacing any that were set with an equivalent field name. + pub fn insert_header(mut self, header: impl TryIntoHeaderPair) -> Self { + self.req.insert_header(header); + self + } + + /// Append a header, keeping any that were set with an equivalent field name. + pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self { + self.req.append_header(header); + self + } + + /// Set cookie for this request. + #[cfg(feature = "cookies")] + pub fn cookie(mut self, cookie: Cookie<'_>) -> Self { + self.cookies.add(cookie.into_owned()); + self + } + + /// Set request path pattern parameter. + /// + /// # Examples + /// ``` + /// use actix_web::test::TestRequest; + /// + /// let req = TestRequest::default().param("foo", "bar"); + /// let req = TestRequest::default().param("foo".to_owned(), "bar".to_owned()); + /// ``` + pub fn param( + mut self, + name: impl Into>, + value: impl Into>, + ) -> Self { + self.path.add_static(name, value); + self + } + + /// Set peer addr. + pub fn peer_addr(mut self, addr: SocketAddr) -> Self { + self.peer_addr = Some(addr); + self + } + + /// Set request payload. + pub fn set_payload>(mut self, data: B) -> Self { + self.req.set_payload(data); + self + } + + /// Serialize `data` to a URL encoded form and set it as the request payload. The `Content-Type` + /// header is set to `application/x-www-form-urlencoded`. + pub fn set_form(mut self, data: &T) -> Self { + let bytes = serde_urlencoded::to_string(data) + .expect("Failed to serialize test data as a urlencoded form"); + self.req.set_payload(bytes); + self.req.insert_header(ContentType::form_url_encoded()); + self + } + + /// Serialize `data` to JSON and set it as the request payload. The `Content-Type` header is + /// set to `application/json`. + pub fn set_json(mut self, data: &T) -> Self { + let bytes = serde_json::to_string(data).expect("Failed to serialize test data to json"); + self.req.set_payload(bytes); + self.req.insert_header(ContentType::json()); + self + } + + /// Set application data. This is equivalent of `App::data()` method + /// for testing purpose. + pub fn data(mut self, data: T) -> Self { + self.app_data.insert(Data::new(data)); + self + } + + /// Set application data. This is equivalent of `App::app_data()` method + /// for testing purpose. + pub fn app_data(mut self, data: T) -> Self { + self.app_data.insert(data); + self + } + + #[cfg(test)] + /// Set request config + pub(crate) fn rmap(mut self, rmap: ResourceMap) -> Self { + self.rmap = rmap; + self + } + + fn finish(&mut self) -> Request { + // mut used when cookie feature is enabled + #[allow(unused_mut)] + let mut req = self.req.finish(); + + #[cfg(feature = "cookies")] + { + use actix_http::header::{HeaderValue, COOKIE}; + + let cookie: String = self + .cookies + .delta() + // ensure only name=value is written to cookie header + .map(|c| c.stripped().encoded().to_string()) + .collect::>() + .join("; "); + + if !cookie.is_empty() { + req.headers_mut() + .insert(COOKIE, HeaderValue::from_str(&cookie).unwrap()); + } + } + + req + } + + /// Complete request creation and generate `Request` instance + pub fn to_request(mut self) -> Request { + let mut req = self.finish(); + req.head_mut().peer_addr = self.peer_addr; + req + } + + /// Complete request creation and generate `ServiceRequest` instance + pub fn to_srv_request(mut self) -> ServiceRequest { + let (mut head, payload) = self.finish().into_parts(); + head.peer_addr = self.peer_addr; + self.path.get_mut().update(&head.uri); + + let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone()); + + ServiceRequest::new( + HttpRequest::new( + self.path, + head, + app_state, + Rc::new(self.app_data), + None, + Default::default(), + ), + payload, + ) + } + + /// Complete request creation and generate `ServiceResponse` instance + pub fn to_srv_response(self, res: HttpResponse) -> ServiceResponse { + self.to_srv_request().into_response(res) + } + + /// Complete request creation and generate `HttpRequest` instance + pub fn to_http_request(mut self) -> HttpRequest { + let (mut head, _) = self.finish().into_parts(); + head.peer_addr = self.peer_addr; + self.path.get_mut().update(&head.uri); + + let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone()); + + HttpRequest::new( + self.path, + head, + app_state, + Rc::new(self.app_data), + None, + Default::default(), + ) + } + + /// Complete request creation and generate `HttpRequest` and `Payload` instances + pub fn to_http_parts(mut self) -> (HttpRequest, Payload) { + let (mut head, payload) = self.finish().into_parts(); + head.peer_addr = self.peer_addr; + self.path.get_mut().update(&head.uri); + + let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone()); + + let req = HttpRequest::new( + self.path, + head, + app_state, + Rc::new(self.app_data), + None, + Default::default(), + ); + + (req, payload) + } + + /// Complete request creation, calls service and waits for response future completion. + pub async fn send_request(self, app: &S) -> S::Response + where + S: Service, Error = E>, + E: std::fmt::Debug, + { + let req = self.to_request(); + test::call_service(app, req).await + } + + #[cfg(test)] + pub fn set_server_hostname(&mut self, host: &str) { + self.config.set_host(host) + } +} + +#[cfg(test)] +mod tests { + use std::time::SystemTime; + + use super::*; + use crate::{http::header, test::init_service, web, App, Error, HttpResponse, Responder}; + + #[actix_rt::test] + async fn test_basics() { + let req = TestRequest::default() + .version(Version::HTTP_2) + .insert_header(header::ContentType::json()) + .insert_header(header::Date(SystemTime::now().into())) + .param("test", "123") + .data(10u32) + .app_data(20u64) + .peer_addr("127.0.0.1:8081".parse().unwrap()) + .to_http_request(); + assert!(req.headers().contains_key(header::CONTENT_TYPE)); + assert!(req.headers().contains_key(header::DATE)); + assert_eq!( + req.head().peer_addr, + Some("127.0.0.1:8081".parse().unwrap()) + ); + assert_eq!(&req.match_info()["test"], "123"); + assert_eq!(req.version(), Version::HTTP_2); + let data = req.app_data::>().unwrap(); + assert!(req.app_data::>().is_none()); + assert_eq!(*data.get_ref(), 10); + + assert!(req.app_data::().is_none()); + let data = req.app_data::().unwrap(); + assert_eq!(*data, 20); + } + + #[actix_rt::test] + async fn test_send_request() { + let app = init_service( + App::new().service( + web::resource("/index.html") + .route(web::get().to(|| HttpResponse::Ok().body("welcome!"))), + ), + ) + .await; + + let resp = TestRequest::get() + .uri("/index.html") + .send_request(&app) + .await; + + let result = test::read_body(resp).await; + assert_eq!(result, Bytes::from_static(b"welcome!")); + } + + #[actix_rt::test] + async fn test_async_with_block() { + async fn async_with_block() -> Result { + let res = web::block(move || Some(4usize).ok_or("wrong")).await; + + match res { + Ok(value) => Ok(HttpResponse::Ok() + .content_type("text/plain") + .body(format!("Async with block value: {:?}", value))), + Err(_) => panic!("Unexpected"), + } + } + + let app = + init_service(App::new().service(web::resource("/index.html").to(async_with_block))) + .await; + + let req = TestRequest::post().uri("/index.html").to_request(); + let res = app.call(req).await.unwrap(); + assert!(res.status().is_success()); + } + + // allow deprecated App::data + #[allow(deprecated)] + #[actix_rt::test] + async fn test_server_data() { + async fn handler(data: web::Data) -> impl Responder { + assert_eq!(**data, 10); + HttpResponse::Ok() + } + + let app = init_service( + App::new() + .data(10usize) + .service(web::resource("/index.html").to(handler)), + ) + .await; + + let req = TestRequest::post().uri("/index.html").to_request(); + let res = app.call(req).await.unwrap(); + assert!(res.status().is_success()); + } +} diff --git a/src/test/test_services.rs b/src/test/test_services.rs new file mode 100644 index 000000000..b4810cfd8 --- /dev/null +++ b/src/test/test_services.rs @@ -0,0 +1,31 @@ +use actix_utils::future::ok; + +use crate::{ + body::BoxBody, + dev::{fn_service, Service, ServiceRequest, ServiceResponse}, + http::StatusCode, + Error, HttpResponseBuilder, +}; + +/// Creates service that always responds with `200 OK` and no body. +pub fn ok_service( +) -> impl Service, Error = Error> { + simple_service(StatusCode::OK) +} + +/// Creates service that always responds with given status code and no body. +pub fn simple_service( + status_code: StatusCode, +) -> impl Service, Error = Error> { + fn_service(move |req: ServiceRequest| { + ok(req.into_response(HttpResponseBuilder::new(status_code).finish())) + }) +} + +#[doc(hidden)] +#[deprecated(since = "4.0.0", note = "Renamed to `simple_service`.")] +pub fn default_service( + status_code: StatusCode, +) -> impl Service, Error = Error> { + simple_service(status_code) +} diff --git a/src/test/test_utils.rs b/src/test/test_utils.rs new file mode 100644 index 000000000..02d4c9bf3 --- /dev/null +++ b/src/test/test_utils.rs @@ -0,0 +1,474 @@ +use std::fmt; + +use actix_http::Request; +use actix_service::IntoServiceFactory; +use serde::de::DeserializeOwned; + +use crate::{ + body::{self, MessageBody}, + config::AppConfig, + dev::{Service, ServiceFactory}, + service::ServiceResponse, + web::Bytes, + Error, +}; + +/// Initialize service from application builder instance. +/// +/// # Examples +/// ``` +/// use actix_service::Service; +/// use actix_web::{test, web, App, HttpResponse, http::StatusCode}; +/// +/// #[actix_web::test] +/// async fn test_init_service() { +/// let app = test::init_service( +/// App::new() +/// .service(web::resource("/test").to(|| async { "OK" })) +/// ).await; +/// +/// // Create request object +/// let req = test::TestRequest::with_uri("/test").to_request(); +/// +/// // Execute application +/// let res = app.call(req).await.unwrap(); +/// assert_eq!(res.status(), StatusCode::OK); +/// } +/// ``` +/// +/// # Panics +/// Panics if service initialization returns an error. +pub async fn init_service( + app: R, +) -> impl Service, Error = E> +where + R: IntoServiceFactory, + S: ServiceFactory, Error = E>, + S::InitError: std::fmt::Debug, +{ + try_init_service(app) + .await + .expect("service initialization failed") +} + +/// Fallible version of [`init_service`] that allows testing initialization errors. +pub(crate) async fn try_init_service( + app: R, +) -> Result, Error = E>, S::InitError> +where + R: IntoServiceFactory, + S: ServiceFactory, Error = E>, + S::InitError: std::fmt::Debug, +{ + let srv = app.into_factory(); + srv.new_service(AppConfig::default()).await +} + +/// Calls service and waits for response future completion. +/// +/// # Examples +/// ``` +/// use actix_web::{test, web, App, HttpResponse, http::StatusCode}; +/// +/// #[actix_web::test] +/// async fn test_response() { +/// let app = test::init_service( +/// App::new() +/// .service(web::resource("/test").to(|| async { +/// HttpResponse::Ok() +/// })) +/// ).await; +/// +/// // Create request object +/// let req = test::TestRequest::with_uri("/test").to_request(); +/// +/// // Call application +/// let res = test::call_service(&app, req).await; +/// assert_eq!(res.status(), StatusCode::OK); +/// } +/// ``` +/// +/// # Panics +/// Panics if service call returns error. +pub async fn call_service(app: &S, req: R) -> S::Response +where + S: Service, Error = E>, + E: std::fmt::Debug, +{ + app.call(req) + .await + .expect("test service call returned error") +} + +/// Helper function that returns a response body of a TestRequest +/// +/// # Examples +/// ``` +/// use actix_web::{test, web, App, HttpResponse, http::header}; +/// use bytes::Bytes; +/// +/// #[actix_web::test] +/// async fn test_index() { +/// let app = test::init_service( +/// App::new().service( +/// web::resource("/index.html") +/// .route(web::post().to(|| async { +/// HttpResponse::Ok().body("welcome!") +/// }))) +/// ).await; +/// +/// let req = test::TestRequest::post() +/// .uri("/index.html") +/// .header(header::CONTENT_TYPE, "application/json") +/// .to_request(); +/// +/// let result = test::call_and_read_body(&app, req).await; +/// assert_eq!(result, Bytes::from_static(b"welcome!")); +/// } +/// ``` +/// +/// # Panics +/// Panics if: +/// - service call returns error; +/// - body yields an error while it is being read. +pub async fn call_and_read_body(app: &S, req: Request) -> Bytes +where + S: Service, Error = Error>, + B: MessageBody, + B::Error: fmt::Debug, +{ + let res = call_service(app, req).await; + read_body(res).await +} + +#[doc(hidden)] +#[deprecated(since = "4.0.0", note = "Renamed to `call_and_read_body`.")] +pub async fn read_response(app: &S, req: Request) -> Bytes +where + S: Service, Error = Error>, + B: MessageBody, + B::Error: fmt::Debug, +{ + let res = call_service(app, req).await; + read_body(res).await +} + +/// Helper function that returns a response body of a ServiceResponse. +/// +/// # Examples +/// ``` +/// use actix_web::{test, web, App, HttpResponse, http::header}; +/// use bytes::Bytes; +/// +/// #[actix_web::test] +/// async fn test_index() { +/// let app = test::init_service( +/// App::new().service( +/// web::resource("/index.html") +/// .route(web::post().to(|| async { +/// HttpResponse::Ok().body("welcome!") +/// }))) +/// ).await; +/// +/// let req = test::TestRequest::post() +/// .uri("/index.html") +/// .header(header::CONTENT_TYPE, "application/json") +/// .to_request(); +/// +/// let res = test::call_service(&app, req).await; +/// let result = test::read_body(res).await; +/// assert_eq!(result, Bytes::from_static(b"welcome!")); +/// } +/// ``` +/// +/// # Panics +/// Panics if body yields an error while it is being read. +pub async fn read_body(res: ServiceResponse) -> Bytes +where + B: MessageBody, + B::Error: fmt::Debug, +{ + let body = res.into_body(); + body::to_bytes(body) + .await + .expect("error reading test response body") +} + +/// Helper function that returns a deserialized response body of a ServiceResponse. +/// +/// # Examples +/// ``` +/// use actix_web::{App, test, web, HttpResponse, http::header}; +/// use serde::{Serialize, Deserialize}; +/// +/// #[derive(Serialize, Deserialize)] +/// pub struct Person { +/// id: String, +/// name: String, +/// } +/// +/// #[actix_web::test] +/// async fn test_post_person() { +/// let app = test::init_service( +/// App::new().service( +/// web::resource("/people") +/// .route(web::post().to(|person: web::Json| async { +/// HttpResponse::Ok() +/// .json(person)}) +/// )) +/// ).await; +/// +/// let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); +/// +/// let res = test::TestRequest::post() +/// .uri("/people") +/// .header(header::CONTENT_TYPE, "application/json") +/// .set_payload(payload) +/// .send_request(&mut app) +/// .await; +/// +/// assert!(res.status().is_success()); +/// +/// let result: Person = test::read_body_json(res).await; +/// } +/// ``` +/// +/// # Panics +/// Panics if: +/// - body yields an error while it is being read; +/// - received body is not a valid JSON representation of `T`. +pub async fn read_body_json(res: ServiceResponse) -> T +where + B: MessageBody, + B::Error: fmt::Debug, + T: DeserializeOwned, +{ + let body = read_body(res).await; + + serde_json::from_slice(&body).unwrap_or_else(|err| { + panic!( + "could not deserialize body into a {}\nerr: {}\nbody: {:?}", + std::any::type_name::(), + err, + body, + ) + }) +} + +/// Helper function that returns a deserialized response body of a TestRequest +/// +/// # Examples +/// ``` +/// use actix_web::{App, test, web, HttpResponse, http::header}; +/// use serde::{Serialize, Deserialize}; +/// +/// #[derive(Serialize, Deserialize)] +/// pub struct Person { +/// id: String, +/// name: String +/// } +/// +/// #[actix_web::test] +/// async fn test_add_person() { +/// let app = test::init_service( +/// App::new().service( +/// web::resource("/people") +/// .route(web::post().to(|person: web::Json| async { +/// HttpResponse::Ok() +/// .json(person)}) +/// )) +/// ).await; +/// +/// let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); +/// +/// let req = test::TestRequest::post() +/// .uri("/people") +/// .header(header::CONTENT_TYPE, "application/json") +/// .set_payload(payload) +/// .to_request(); +/// +/// let result: Person = test::call_and_read_body_json(&mut app, req).await; +/// } +/// ``` +/// +/// # Panics +/// Panics if: +/// - service call returns an error body yields an error while it is being read; +/// - body yields an error while it is being read; +/// - received body is not a valid JSON representation of `T`. +pub async fn call_and_read_body_json(app: &S, req: Request) -> T +where + S: Service, Error = Error>, + B: MessageBody, + B::Error: fmt::Debug, + T: DeserializeOwned, +{ + let res = call_service(app, req).await; + read_body_json(res).await +} + +#[doc(hidden)] +#[deprecated(since = "4.0.0", note = "Renamed to `call_and_read_body_json`.")] +pub async fn read_response_json(app: &S, req: Request) -> T +where + S: Service, Error = Error>, + B: MessageBody, + B::Error: fmt::Debug, + T: DeserializeOwned, +{ + call_and_read_body_json(app, req).await +} + +#[cfg(test)] +mod tests { + + use serde::{Deserialize, Serialize}; + + use super::*; + use crate::{http::header, test::TestRequest, web, App, HttpMessage, HttpResponse}; + + #[actix_rt::test] + async fn test_request_methods() { + let app = init_service( + App::new().service( + web::resource("/index.html") + .route(web::put().to(|| HttpResponse::Ok().body("put!"))) + .route(web::patch().to(|| HttpResponse::Ok().body("patch!"))) + .route(web::delete().to(|| HttpResponse::Ok().body("delete!"))), + ), + ) + .await; + + let put_req = TestRequest::put() + .uri("/index.html") + .insert_header((header::CONTENT_TYPE, "application/json")) + .to_request(); + + let result = call_and_read_body(&app, put_req).await; + assert_eq!(result, Bytes::from_static(b"put!")); + + let patch_req = TestRequest::patch() + .uri("/index.html") + .insert_header((header::CONTENT_TYPE, "application/json")) + .to_request(); + + let result = call_and_read_body(&app, patch_req).await; + assert_eq!(result, Bytes::from_static(b"patch!")); + + let delete_req = TestRequest::delete().uri("/index.html").to_request(); + let result = call_and_read_body(&app, delete_req).await; + assert_eq!(result, Bytes::from_static(b"delete!")); + } + + #[derive(Serialize, Deserialize)] + pub struct Person { + id: String, + name: String, + } + + #[actix_rt::test] + async fn test_response_json() { + let app = init_service(App::new().service(web::resource("/people").route( + web::post().to(|person: web::Json| HttpResponse::Ok().json(person)), + ))) + .await; + + let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); + + let req = TestRequest::post() + .uri("/people") + .insert_header((header::CONTENT_TYPE, "application/json")) + .set_payload(payload) + .to_request(); + + let result: Person = call_and_read_body_json(&app, req).await; + assert_eq!(&result.id, "12345"); + } + + #[actix_rt::test] + async fn test_body_json() { + let app = init_service(App::new().service(web::resource("/people").route( + web::post().to(|person: web::Json| HttpResponse::Ok().json(person)), + ))) + .await; + + let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); + + let res = TestRequest::post() + .uri("/people") + .insert_header((header::CONTENT_TYPE, "application/json")) + .set_payload(payload) + .send_request(&app) + .await; + + let result: Person = read_body_json(res).await; + assert_eq!(&result.name, "User name"); + } + + #[actix_rt::test] + async fn test_request_response_form() { + let app = init_service(App::new().service(web::resource("/people").route( + web::post().to(|person: web::Form| HttpResponse::Ok().json(person)), + ))) + .await; + + let payload = Person { + id: "12345".to_string(), + name: "User name".to_string(), + }; + + let req = TestRequest::post() + .uri("/people") + .set_form(&payload) + .to_request(); + + assert_eq!(req.content_type(), "application/x-www-form-urlencoded"); + + let result: Person = call_and_read_body_json(&app, req).await; + assert_eq!(&result.id, "12345"); + assert_eq!(&result.name, "User name"); + } + + #[actix_rt::test] + async fn test_response() { + let app = init_service( + App::new().service( + web::resource("/index.html") + .route(web::post().to(|| HttpResponse::Ok().body("welcome!"))), + ), + ) + .await; + + let req = TestRequest::post() + .uri("/index.html") + .insert_header((header::CONTENT_TYPE, "application/json")) + .to_request(); + + let result = call_and_read_body(&app, req).await; + assert_eq!(result, Bytes::from_static(b"welcome!")); + } + + #[actix_rt::test] + async fn test_request_response_json() { + let app = init_service(App::new().service(web::resource("/people").route( + web::post().to(|person: web::Json| HttpResponse::Ok().json(person)), + ))) + .await; + + let payload = Person { + id: "12345".to_string(), + name: "User name".to_string(), + }; + + let req = TestRequest::post() + .uri("/people") + .set_json(&payload) + .to_request(); + + assert_eq!(req.content_type(), "application/json"); + + let result: Person = call_and_read_body_json(&app, req).await; + assert_eq!(&result.id, "12345"); + assert_eq!(&result.name, "User name"); + } +} diff --git a/src/types/either.rs b/src/types/either.rs index 5b8e02525..0eafb9e43 100644 --- a/src/types/either.rs +++ b/src/types/either.rs @@ -20,8 +20,6 @@ use crate::{ /// Combines two extractor or responder types into a single type. /// -/// Can be converted to and from an [`either::Either`]. -/// /// # Extractor /// Provides a mechanism for trying two extractors, a primary and a fallback. Useful for /// "polymorphic payloads" where, for example, a form might be JSON or URL encoded. diff --git a/src/types/json.rs b/src/types/json.rs index 2b4d220e2..be6078b2b 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -449,12 +449,13 @@ mod tests { use super::*; use crate::{ + body, error::InternalError, http::{ header::{self, CONTENT_LENGTH, CONTENT_TYPE}, StatusCode, }, - test::{assert_body_eq, load_body, TestRequest}, + test::{assert_body_eq, TestRequest}, }; #[derive(Serialize, Deserialize, PartialEq, Debug)] @@ -517,7 +518,7 @@ mod tests { let resp = HttpResponse::from_error(s.err().unwrap()); assert_eq!(resp.status(), StatusCode::BAD_REQUEST); - let body = load_body(resp.into_body()).await.unwrap(); + let body = body::to_bytes(resp.into_body()).await.unwrap(); let msg: MyObject = serde_json::from_slice(&body).unwrap(); assert_eq!(msg.name, "invalid request"); } From a6d5776481eccf50819a2a64953ef4c954f1dcf9 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Fri, 17 Dec 2021 01:25:10 +0300 Subject: [PATCH 33/87] various fixes to MessageBody::complete_body (#2519) --- actix-http/src/body/boxed.rs | 22 +--------------- actix-http/src/body/message_body.rs | 39 ++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/actix-http/src/body/boxed.rs b/actix-http/src/body/boxed.rs index d2469e986..d4737aab8 100644 --- a/actix-http/src/body/boxed.rs +++ b/actix-http/src/body/boxed.rs @@ -57,27 +57,7 @@ impl MessageBody for BoxBody { } fn take_complete_body(&mut self) -> Bytes { - debug_assert!( - self.is_complete_body(), - "boxed type does not allow taking complete body; caller should make sure to \ - call `is_complete_body` first", - ); - - // we do not have DerefMut access to call take_complete_body directly but since - // is_complete_body is true we should expect the entire bytes chunk in one poll_next - - let waker = futures_util::task::noop_waker(); - let mut cx = Context::from_waker(&waker); - - match self.as_pin_mut().poll_next(&mut cx) { - Poll::Ready(Some(Ok(data))) => data, - _ => { - panic!( - "boxed type indicated it allows taking complete body but failed to \ - return Bytes when polled", - ); - } - } + self.0.take_complete_body() } } diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index 3e6c8d5cb..20263b3fb 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -134,7 +134,7 @@ mod foreign_impls { impl MessageBody for Box where - B: MessageBody + Unpin, + B: MessageBody + Unpin + ?Sized, { type Error = B::Error; @@ -164,7 +164,7 @@ mod foreign_impls { impl MessageBody for Pin> where - B: MessageBody, + B: MessageBody + ?Sized, { type Error = B::Error; @@ -175,10 +175,10 @@ mod foreign_impls { #[inline] fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - self.as_mut().poll_next(cx) + self.get_mut().as_mut().poll_next(cx) } #[inline] @@ -475,6 +475,16 @@ where None => Poll::Ready(None), } } + + #[inline] + fn is_complete_body(&self) -> bool { + self.body.is_complete_body() + } + + #[inline] + fn take_complete_body(&mut self) -> Bytes { + self.body.take_complete_body() + } } #[cfg(test)] @@ -630,6 +640,27 @@ mod tests { assert_eq!(Pin::new(&mut data).poll_next(&mut cx), Poll::Ready(None)); } + #[test] + fn complete_body_combinators() { + use crate::body::{BoxBody, EitherBody}; + + let body = Bytes::from_static(b"test"); + let body = BoxBody::new(body); + let body = EitherBody::<_, ()>::left(body); + let body = EitherBody::<(), _>::right(body); + let body = Box::new(body); + let body = Box::pin(body); + let mut body = body; + + assert!(body.is_complete_body()); + assert_eq!(body.take_complete_body(), b"test".as_ref()); + + // subsequent poll_next returns None + let waker = futures_util::task::noop_waker(); + let mut cx = Context::from_waker(&waker); + assert!(Pin::new(&mut body).poll_next(&mut cx).map_err(drop) == Poll::Ready(None)); + } + // down-casting used to be done with a method on MessageBody trait // test is kept to demonstrate equivalence of Any trait #[actix_rt::test] From 44b7302845e163805fd12a445c34816d43cf98ef Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 16 Dec 2021 22:26:45 +0000 Subject: [PATCH 34/87] minimize futures-util dep in actix-http --- actix-http/Cargo.toml | 3 ++- actix-http/src/body/message_body.rs | 4 ++-- awc/Cargo.toml | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 374a55a62..2e8ec1dfc 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -55,7 +55,7 @@ bytestring = "1" derive_more = "0.99.5" encoding_rs = "0.8" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } -futures-util = { version = "0.3.7", default-features = false, features = ["alloc", "sink"] } +futures-task = { version = "0.3.7", default-features = false, features = ["alloc"] } h2 = "0.3.9" http = "0.2.5" httparse = "1.5.1" @@ -89,6 +89,7 @@ actix-web = "4.0.0-beta.14" async-stream = "0.3" criterion = { version = "0.3", features = ["html_reports"] } env_logger = "0.9" +futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] } rcgen = "0.8" regex = "1.3" rustls-pemfile = "0.2" diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index 20263b3fb..10a7260f4 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -198,7 +198,7 @@ mod foreign_impls { // we do not have DerefMut access to call take_complete_body directly but since // is_complete_body is true we should expect the entire bytes chunk in one poll_next - let waker = futures_util::task::noop_waker(); + let waker = futures_task::noop_waker(); let mut cx = Context::from_waker(&waker); match self.as_mut().poll_next(&mut cx) { @@ -631,7 +631,7 @@ mod tests { // second call returns empty assert_eq!(data.take_complete_body(), b"".as_ref()); - let waker = futures_util::task::noop_waker(); + let waker = futures_task::noop_waker(); let mut cx = Context::from_waker(&waker); let mut data = Bytes::from_static(b"test"); // take returns whole chunk diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 48ae27df0..60a95871c 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -70,8 +70,8 @@ base64 = "0.13" bytes = "1" cfg-if = "1" derive_more = "0.99.5" -futures-core = { version = "0.3.7", default-features = false } -futures-util = { version = "0.3.7", default-features = false } +futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } +futures-util = { version = "0.3.7", default-features = false, features = ["alloc", "sink"] } h2 = "0.3.9" http = "0.2.5" itoa = "0.4" From 3c0d059d92aa06be9e3a5b9216977dd3b7572f91 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Fri, 17 Dec 2021 03:43:40 +0300 Subject: [PATCH 35/87] MessageBody::boxed (#2520) Co-authored-by: Rob Ede --- actix-http/CHANGES.md | 2 ++ actix-http/src/body/boxed.rs | 16 +++++++++++++++- actix-http/src/body/either.rs | 8 ++++++++ actix-http/src/body/message_body.rs | 13 +++++++++++-- actix-http/src/response.rs | 2 +- awc/src/any_body.rs | 4 +--- src/response/response.rs | 3 +-- src/service.rs | 2 +- 8 files changed, 40 insertions(+), 10 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 011e2c608..598ef9c0e 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -28,6 +28,7 @@ * `Request::take_req_data()`. [#2487] * `impl Clone` for `RequestHead`. [#2487] * New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimisations on body types that are done in exactly one poll/chunk. [#2497] +* New `boxed` method on `MessageBody` trait for wrapping body type. [#2520] ### Changed * Rename `body::BoxBody::{from_body => new}`. [#2468] @@ -56,6 +57,7 @@ [#2488]: https://github.com/actix/actix-web/pull/2488 [#2491]: https://github.com/actix/actix-web/pull/2491 [#2497]: https://github.com/actix/actix-web/pull/2497 +[#2520]: https://github.com/actix/actix-web/pull/2520 ## 3.0.0-beta.14 - 2021-11-30 diff --git a/actix-http/src/body/boxed.rs b/actix-http/src/body/boxed.rs index d4737aab8..7581bec88 100644 --- a/actix-http/src/body/boxed.rs +++ b/actix-http/src/body/boxed.rs @@ -14,7 +14,11 @@ use crate::Error; pub struct BoxBody(Pin>>>); impl BoxBody { - /// Boxes a `MessageBody` and any errors it generates. + /// Same as `MessageBody::boxed`. + /// + /// If the body type to wrap is unknown or generic it is better to use [`MessageBody::boxed`] to + /// avoid double boxing. + #[inline] pub fn new(body: B) -> Self where B: MessageBody + 'static, @@ -24,6 +28,7 @@ impl BoxBody { } /// Returns a mutable pinned reference to the inner message body type. + #[inline] pub fn as_pin_mut(&mut self) -> Pin<&mut (dyn MessageBody>)> { self.0.as_mut() } @@ -38,10 +43,12 @@ impl fmt::Debug for BoxBody { impl MessageBody for BoxBody { type Error = Error; + #[inline] fn size(&self) -> BodySize { self.0.size() } + #[inline] fn poll_next( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -52,13 +59,20 @@ impl MessageBody for BoxBody { .map_err(|err| Error::new_body().with_cause(err)) } + #[inline] fn is_complete_body(&self) -> bool { self.0.is_complete_body() } + #[inline] fn take_complete_body(&mut self) -> Bytes { self.0.take_complete_body() } + + #[inline] + fn boxed(self) -> BoxBody { + self + } } #[cfg(test)] diff --git a/actix-http/src/body/either.rs b/actix-http/src/body/either.rs index 103b39c5d..3a4082dc9 100644 --- a/actix-http/src/body/either.rs +++ b/actix-http/src/body/either.rs @@ -88,6 +88,14 @@ where EitherBody::Right { body } => body.take_complete_body(), } } + + #[inline] + fn boxed(self) -> BoxBody { + match self { + EitherBody::Left { body } => body.boxed(), + EitherBody::Right { body } => body.boxed(), + } + } } #[cfg(test)] diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index 10a7260f4..075ae7220 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -12,7 +12,7 @@ use bytes::{Bytes, BytesMut}; use futures_core::ready; use pin_project_lite::pin_project; -use super::BodySize; +use super::{BodySize, BoxBody}; /// An interface types that can converted to bytes and used as response bodies. // TODO: examples @@ -77,6 +77,15 @@ pub trait MessageBody { std::any::type_name::() ); } + + /// Converts this body into `BoxBody`. + #[inline] + fn boxed(self) -> BoxBody + where + Self: Sized + 'static, + { + BoxBody::new(self) + } } mod foreign_impls { @@ -656,7 +665,7 @@ mod tests { assert_eq!(body.take_complete_body(), b"test".as_ref()); // subsequent poll_next returns None - let waker = futures_util::task::noop_waker(); + let waker = futures_task::noop_waker(); let mut cx = Context::from_waker(&waker); assert!(Pin::new(&mut body).poll_next(&mut cx).map_err(drop) == Poll::Ready(None)); } diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index 9f799f669..aee9e80b4 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -194,7 +194,7 @@ impl Response { where B: MessageBody + 'static, { - self.map_body(|_, body| BoxBody::new(body)) + self.map_body(|_, body| body.boxed()) } /// Returns body, consuming this response. diff --git a/awc/src/any_body.rs b/awc/src/any_body.rs index cb9038ff3..2ffeb5074 100644 --- a/awc/src/any_body.rs +++ b/awc/src/any_body.rs @@ -45,9 +45,7 @@ impl AnyBody { where B: MessageBody + 'static, { - Self::Body { - body: BoxBody::new(body), - } + Self::Body { body: body.boxed() } } /// Constructs new `AnyBody` instance from a slice of bytes by copying it. diff --git a/src/response/response.rs b/src/response/response.rs index 1900dd845..4fb4b44b6 100644 --- a/src/response/response.rs +++ b/src/response/response.rs @@ -244,8 +244,7 @@ impl HttpResponse { where B: MessageBody + 'static, { - // TODO: avoid double boxing with down-casting, if it improves perf - self.map_body(|_, body| BoxBody::new(body)) + self.map_body(|_, body| body.boxed()) } /// Extract response body diff --git a/src/service.rs b/src/service.rs index 36b3858e6..9ccf5274d 100644 --- a/src/service.rs +++ b/src/service.rs @@ -451,7 +451,7 @@ impl ServiceResponse { where B: MessageBody + 'static, { - self.map_body(|_, body| BoxBody::new(body)) + self.map_body(|_, body| body.boxed()) } } From a2467718ac14d4ecf294ab1b2a398c47e97d47fa Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Dec 2021 01:27:27 +0000 Subject: [PATCH 36/87] passthrough StreamLog error type --- src/middleware/logger.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index 74daa26d5..d7fdb234f 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -322,13 +322,10 @@ pin_project! { } } -impl MessageBody for StreamLog -where - B: MessageBody, - B::Error: Into, -{ - type Error = Error; +impl MessageBody for StreamLog { + type Error = B::Error; + #[inline] fn size(&self) -> BodySize { self.body.size() } @@ -344,7 +341,7 @@ where *this.size += chunk.len(); Poll::Ready(Some(Ok(chunk))) } - Some(Err(err)) => Poll::Ready(Some(Err(err.into()))), + Some(Err(err)) => Poll::Ready(Some(Err(err))), None => Poll::Ready(None), } } From 5359fa56c277362ae689f5170d36b74228291fb5 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Dec 2021 01:29:41 +0000 Subject: [PATCH 37/87] include source for dispatch body errors --- actix-http/src/error.rs | 49 ++++++++++++++++----------------- actix-http/src/h1/dispatcher.rs | 2 +- actix-http/src/h1/service.rs | 6 ++-- actix-http/src/service.rs | 6 ++-- 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index a04867ae1..3d2a918f4 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -332,31 +332,28 @@ impl From for Error { } /// A set of errors that can occur during dispatching HTTP requests. -#[derive(Debug, Display, Error, From)] -#[non_exhaustive] +#[derive(Debug, Display, From)] pub enum DispatchError { - /// Service error - // FIXME: display and error type + /// Service error. #[display(fmt = "Service Error")] - Service(#[error(not(source))] Response), + Service(Response), - /// Body error - // FIXME: display and error type - #[display(fmt = "Body Error")] - Body(#[error(not(source))] Box), + /// Body streaming error. + #[display(fmt = "Body error: {}", _0)] + Body(Box), - /// Upgrade service error + /// Upgrade service error. Upgrade, /// An `io::Error` that occurred while trying to read or write to a network stream. #[display(fmt = "IO error: {}", _0)] Io(io::Error), - /// Http request parse error. - #[display(fmt = "Parse error: {}", _0)] + /// Request parse error. + #[display(fmt = "Request parse error: {}", _0)] Parse(ParseError), - /// Http/2 error + /// HTTP/2 error. #[display(fmt = "{}", _0)] H2(h2::Error), @@ -368,21 +365,23 @@ pub enum DispatchError { #[display(fmt = "Connection shutdown timeout")] DisconnectTimeout, - /// Payload is not consumed - #[display(fmt = "Task is completed but request's payload is not consumed")] - PayloadIsNotConsumed, - - /// Malformed request - #[display(fmt = "Malformed request")] - MalformedRequest, - - /// Internal error + /// Internal error. #[display(fmt = "Internal error")] InternalError, +} - /// Unknown error - #[display(fmt = "Unknown error")] - Unknown, +impl StdError for DispatchError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + // TODO: error source extraction? + DispatchError::Service(_res) => None, + DispatchError::Body(err) => Some(&**err), + DispatchError::Io(err) => Some(err), + DispatchError::Parse(err) => Some(err), + DispatchError::H2(err) => Some(err), + _ => None, + } + } } /// A set of error that can occur during parsing content type. diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 64bf83e03..16d7c3c11 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -458,7 +458,7 @@ where } Poll::Ready(Some(Err(err))) => { - return Err(DispatchError::Service(err.into())) + return Err(DispatchError::Body(err.into())) } Poll::Pending => return Ok(PollResponse::DoNothing), diff --git a/actix-http/src/h1/service.rs b/actix-http/src/h1/service.rs index c4e6e7714..43b7919a7 100644 --- a/actix-http/src/h1/service.rs +++ b/actix-http/src/h1/service.rs @@ -356,9 +356,9 @@ where type Future = Dispatcher; fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { - self._poll_ready(cx).map_err(|e| { - log::error!("HTTP/1 service readiness error: {:?}", e); - DispatchError::Service(e) + self._poll_ready(cx).map_err(|err| { + log::error!("HTTP/1 service readiness error: {:?}", err); + DispatchError::Service(err) }) } diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 93168749d..cd2efe678 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -493,9 +493,9 @@ where type Future = HttpServiceHandlerResponse; fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { - self._poll_ready(cx).map_err(|e| { - log::error!("HTTP service readiness error: {:?}", e); - DispatchError::Service(e) + self._poll_ready(cx).map_err(|err| { + log::error!("HTTP service readiness error: {:?}", err); + DispatchError::Service(err) }) } From 2cf27863cb99a450d35a460c73a70919dfd29470 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Dec 2021 14:13:54 +0000 Subject: [PATCH 38/87] remove direct dep on pin-project in -http (#2524) --- actix-http/Cargo.toml | 3 +- actix-http/src/h1/dispatcher.rs | 278 ++++++++++++++++++-------------- 2 files changed, 158 insertions(+), 123 deletions(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 2e8ec1dfc..515574ab1 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -45,7 +45,7 @@ __compress = [] actix-service = "2.0.0" actix-codec = "0.4.1" actix-utils = "3.0.0" -actix-rt = "2.2" +actix-rt = { version = "2.2", default-features = false } ahash = "0.7" base64 = "0.13" @@ -66,7 +66,6 @@ local-channel = "0.1" log = "0.4" mime = "0.3" percent-encoding = "2.1" -pin-project = "1.0.0" pin-project-lite = "0.2" rand = "0.8" sha-1 = "0.9" diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 16d7c3c11..472845e65 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -15,7 +15,7 @@ use bitflags::bitflags; use bytes::{Buf, BytesMut}; use futures_core::ready; use log::{error, trace}; -use pin_project::pin_project; +use pin_project_lite::pin_project; use crate::{ body::{BodySize, BoxBody, MessageBody}, @@ -46,79 +46,111 @@ bitflags! { } } -#[pin_project] -/// Dispatcher for HTTP/1.1 protocol -pub struct Dispatcher -where - S: Service, - S::Error: Into>, +// there's 2 versions of Dispatcher state because of: +// https://github.com/taiki-e/pin-project-lite/issues/3 +// +// tl;dr: pin-project-lite doesn't play well with other attribute macros - B: MessageBody, +#[cfg(not(test))] +pin_project! { + /// Dispatcher for HTTP/1.1 protocol + pub struct Dispatcher + where + S: Service, + S::Error: Into>, - X: Service, - X::Error: Into>, + B: MessageBody, - U: Service<(Request, Framed), Response = ()>, - U::Error: fmt::Display, -{ - #[pin] - inner: DispatcherState, + X: Service, + X::Error: Into>, - #[cfg(test)] - poll_count: u64, + U: Service<(Request, Framed), Response = ()>, + U::Error: fmt::Display, + { + #[pin] + inner: DispatcherState, + } } -#[pin_project(project = DispatcherStateProj)] -enum DispatcherState -where - S: Service, - S::Error: Into>, +#[cfg(test)] +pin_project! { + /// Dispatcher for HTTP/1.1 protocol + pub struct Dispatcher + where + S: Service, + S::Error: Into>, - B: MessageBody, + B: MessageBody, - X: Service, - X::Error: Into>, + X: Service, + X::Error: Into>, - U: Service<(Request, Framed), Response = ()>, - U::Error: fmt::Display, -{ - Normal(#[pin] InnerDispatcher), - Upgrade(#[pin] U::Future), + U: Service<(Request, Framed), Response = ()>, + U::Error: fmt::Display, + { + #[pin] + inner: DispatcherState, + + // used in tests + poll_count: u64, + } } -#[pin_project(project = InnerDispatcherProj)] -struct InnerDispatcher -where - S: Service, - S::Error: Into>, +pin_project! { + #[project = DispatcherStateProj] + enum DispatcherState + where + S: Service, + S::Error: Into>, - B: MessageBody, + B: MessageBody, - X: Service, - X::Error: Into>, + X: Service, + X::Error: Into>, - U: Service<(Request, Framed), Response = ()>, - U::Error: fmt::Display, -{ - flow: Rc>, - flags: Flags, - peer_addr: Option, - conn_data: Option>, - error: Option, + U: Service<(Request, Framed), Response = ()>, + U::Error: fmt::Display, + { + Normal { #[pin] inner: InnerDispatcher }, + Upgrade { #[pin] fut: U::Future }, + } +} - #[pin] - state: State, - payload: Option, - messages: VecDeque, +pin_project! { + #[project = InnerDispatcherProj] + struct InnerDispatcher + where + S: Service, + S::Error: Into>, - ka_expire: Instant, - #[pin] - ka_timer: Option, + B: MessageBody, - io: Option, - read_buf: BytesMut, - write_buf: BytesMut, - codec: Codec, + X: Service, + X::Error: Into>, + + U: Service<(Request, Framed), Response = ()>, + U::Error: fmt::Display, + { + flow: Rc>, + flags: Flags, + peer_addr: Option, + conn_data: Option>, + error: Option, + + #[pin] + state: State, + payload: Option, + messages: VecDeque, + + ka_expire: Instant, + #[pin] + ka_timer: Option, + + io: Option, + read_buf: BytesMut, + write_buf: BytesMut, + codec: Codec, + } } enum DispatcherMessage { @@ -127,19 +159,21 @@ enum DispatcherMessage { Error(Response<()>), } -#[pin_project(project = StateProj)] -enum State -where - S: Service, - X: Service, +pin_project! { + #[project = StateProj] + enum State + where + S: Service, + X: Service, - B: MessageBody, -{ - None, - ExpectCall(#[pin] X::Future), - ServiceCall(#[pin] S::Future), - SendPayload(#[pin] B), - SendErrorPayload(#[pin] BoxBody), + B: MessageBody, + { + None, + ExpectCall { #[pin] fut: X::Future }, + ServiceCall { #[pin] fut: S::Future }, + SendPayload { #[pin] body: B }, + SendErrorPayload { #[pin] body: BoxBody }, + } } impl State @@ -198,25 +232,27 @@ where }; Dispatcher { - inner: DispatcherState::Normal(InnerDispatcher { - flow, - flags, - peer_addr, - conn_data: conn_data.0.map(Rc::new), - error: None, + inner: DispatcherState::Normal { + inner: InnerDispatcher { + flow, + flags, + peer_addr, + conn_data: conn_data.0.map(Rc::new), + error: None, - state: State::None, - payload: None, - messages: VecDeque::new(), + state: State::None, + payload: None, + messages: VecDeque::new(), - ka_expire, - ka_timer, + ka_expire, + ka_timer, - io: Some(io), - read_buf: BytesMut::with_capacity(HW_BUFFER_SIZE), - write_buf: BytesMut::with_capacity(HW_BUFFER_SIZE), - codec: Codec::new(config), - }), + io: Some(io), + read_buf: BytesMut::with_capacity(HW_BUFFER_SIZE), + write_buf: BytesMut::with_capacity(HW_BUFFER_SIZE), + codec: Codec::new(config), + }, + }, #[cfg(test)] poll_count: 0, @@ -316,7 +352,7 @@ where let size = self.as_mut().send_response_inner(message, &body)?; let state = match size { BodySize::None | BodySize::Sized(0) => State::None, - _ => State::SendPayload(body), + _ => State::SendPayload { body }, }; self.project().state.set(state); Ok(()) @@ -330,7 +366,7 @@ where let size = self.as_mut().send_response_inner(message, &body)?; let state = match size { BodySize::None | BodySize::Sized(0) => State::None, - _ => State::SendErrorPayload(body), + _ => State::SendErrorPayload { body }, }; self.project().state.set(state); Ok(()) @@ -356,12 +392,12 @@ where // Handle `EXPECT: 100-Continue` header if req.head().expect() { // set InnerDispatcher state and continue loop to poll it. - let task = this.flow.expect.call(req); - this.state.set(State::ExpectCall(task)); + let fut = this.flow.expect.call(req); + this.state.set(State::ExpectCall { fut }); } else { // the same as expect call. - let task = this.flow.service.call(req); - this.state.set(State::ServiceCall(task)); + let fut = this.flow.service.call(req); + this.state.set(State::ServiceCall { fut }); }; } @@ -381,7 +417,7 @@ where // all messages are dealt with. None => return Ok(PollResponse::DoNothing), }, - StateProj::ServiceCall(fut) => match fut.poll(cx) { + StateProj::ServiceCall { fut } => match fut.poll(cx) { // service call resolved. send response. Poll::Ready(Ok(res)) => { let (res, body) = res.into().replace_body(()); @@ -407,11 +443,11 @@ where } }, - StateProj::SendPayload(mut stream) => { + StateProj::SendPayload { mut body } => { // keep populate writer buffer until buffer size limit hit, // get blocked or finished. while this.write_buf.len() < super::payload::MAX_BUFFER_SIZE { - match stream.as_mut().poll_next(cx) { + match body.as_mut().poll_next(cx) { Poll::Ready(Some(Ok(item))) => { this.codec .encode(Message::Chunk(Some(item)), this.write_buf)?; @@ -437,13 +473,13 @@ where return Ok(PollResponse::DrainWriteBuf); } - StateProj::SendErrorPayload(mut stream) => { + StateProj::SendErrorPayload { mut body } => { // TODO: de-dupe impl with SendPayload // keep populate writer buffer until buffer size limit hit, // get blocked or finished. while this.write_buf.len() < super::payload::MAX_BUFFER_SIZE { - match stream.as_mut().poll_next(cx) { + match body.as_mut().poll_next(cx) { Poll::Ready(Some(Ok(item))) => { this.codec .encode(Message::Chunk(Some(item)), this.write_buf)?; @@ -469,14 +505,14 @@ where return Ok(PollResponse::DrainWriteBuf); } - StateProj::ExpectCall(fut) => match fut.poll(cx) { + StateProj::ExpectCall { fut } => match fut.poll(cx) { // expect resolved. write continue to buffer and set InnerDispatcher state // to service call. Poll::Ready(Ok(req)) => { this.write_buf .extend_from_slice(b"HTTP/1.1 100 Continue\r\n\r\n"); let fut = this.flow.service.call(req); - this.state.set(State::ServiceCall(fut)); + this.state.set(State::ServiceCall { fut }); } // send expect error as response @@ -502,25 +538,25 @@ where let mut this = self.as_mut().project(); if req.head().expect() { // set dispatcher state so the future is pinned. - let task = this.flow.expect.call(req); - this.state.set(State::ExpectCall(task)); + let fut = this.flow.expect.call(req); + this.state.set(State::ExpectCall { fut }); } else { // the same as above. - let task = this.flow.service.call(req); - this.state.set(State::ServiceCall(task)); + let fut = this.flow.service.call(req); + this.state.set(State::ServiceCall { fut }); }; // eagerly poll the future for once(or twice if expect is resolved immediately). loop { match self.as_mut().project().state.project() { - StateProj::ExpectCall(fut) => { + StateProj::ExpectCall { fut } => { match fut.poll(cx) { // expect is resolved. continue loop and poll the service call branch. Poll::Ready(Ok(req)) => { self.as_mut().send_continue(); let mut this = self.as_mut().project(); - let task = this.flow.service.call(req); - this.state.set(State::ServiceCall(task)); + let fut = this.flow.service.call(req); + this.state.set(State::ServiceCall { fut }); continue; } // future is pending. return Ok(()) to notify that a new state is @@ -536,7 +572,7 @@ where } } } - StateProj::ServiceCall(fut) => { + StateProj::ServiceCall { fut } => { // return no matter the service call future's result. return match fut.poll(cx) { // future is resolved. send response and return a result. On success @@ -901,7 +937,7 @@ where } match this.inner.project() { - DispatcherStateProj::Normal(mut inner) => { + DispatcherStateProj::Normal { mut inner } => { inner.as_mut().poll_keepalive(cx)?; if inner.flags.contains(Flags::SHUTDOWN) { @@ -941,7 +977,7 @@ where self.as_mut() .project() .inner - .set(DispatcherState::Upgrade(upgrade)); + .set(DispatcherState::Upgrade { fut: upgrade }); return self.poll(cx); } }; @@ -993,8 +1029,8 @@ where } } } - DispatcherStateProj::Upgrade(fut) => fut.poll(cx).map_err(|e| { - error!("Upgrade handler error: {}", e); + DispatcherStateProj::Upgrade { fut: upgrade } => upgrade.poll(cx).map_err(|err| { + error!("Upgrade handler error: {}", err); DispatchError::Upgrade }), } @@ -1088,7 +1124,7 @@ mod tests { Poll::Ready(res) => assert!(res.is_err()), } - if let DispatcherStateProj::Normal(inner) = h1.project().inner.project() { + if let DispatcherStateProj::Normal { inner } = h1.project().inner.project() { assert!(inner.flags.contains(Flags::READ_DISCONNECT)); assert_eq!( &inner.project().io.take().unwrap().write_buf[..26], @@ -1123,7 +1159,7 @@ mod tests { actix_rt::pin!(h1); - assert!(matches!(&h1.inner, DispatcherState::Normal(_))); + assert!(matches!(&h1.inner, DispatcherState::Normal { .. })); match h1.as_mut().poll(cx) { Poll::Pending => panic!("first poll should not be pending"), @@ -1133,7 +1169,7 @@ mod tests { // polls: initial => shutdown assert_eq!(h1.poll_count, 2); - if let DispatcherStateProj::Normal(inner) = h1.project().inner.project() { + if let DispatcherStateProj::Normal { inner } = h1.project().inner.project() { let res = &mut inner.project().io.take().unwrap().write_buf[..]; stabilize_date_header(res); @@ -1177,7 +1213,7 @@ mod tests { actix_rt::pin!(h1); - assert!(matches!(&h1.inner, DispatcherState::Normal(_))); + assert!(matches!(&h1.inner, DispatcherState::Normal { .. })); match h1.as_mut().poll(cx) { Poll::Pending => panic!("first poll should not be pending"), @@ -1187,7 +1223,7 @@ mod tests { // polls: initial => shutdown assert_eq!(h1.poll_count, 1); - if let DispatcherStateProj::Normal(inner) = h1.project().inner.project() { + if let DispatcherStateProj::Normal { inner } = h1.project().inner.project() { let res = &mut inner.project().io.take().unwrap().write_buf[..]; stabilize_date_header(res); @@ -1237,13 +1273,13 @@ mod tests { actix_rt::pin!(h1); assert!(h1.as_mut().poll(cx).is_pending()); - assert!(matches!(&h1.inner, DispatcherState::Normal(_))); + assert!(matches!(&h1.inner, DispatcherState::Normal { .. })); // polls: manual assert_eq!(h1.poll_count, 1); eprintln!("poll count: {}", h1.poll_count); - if let DispatcherState::Normal(ref inner) = h1.inner { + if let DispatcherState::Normal { ref inner } = h1.inner { let io = inner.io.as_ref().unwrap(); let res = &io.write_buf()[..]; assert_eq!( @@ -1258,7 +1294,7 @@ mod tests { // polls: manual manual shutdown assert_eq!(h1.poll_count, 3); - if let DispatcherState::Normal(ref inner) = h1.inner { + if let DispatcherState::Normal { ref inner } = h1.inner { let io = inner.io.as_ref().unwrap(); let mut res = (&io.write_buf()[..]).to_owned(); stabilize_date_header(&mut res); @@ -1309,12 +1345,12 @@ mod tests { actix_rt::pin!(h1); assert!(h1.as_mut().poll(cx).is_ready()); - assert!(matches!(&h1.inner, DispatcherState::Normal(_))); + assert!(matches!(&h1.inner, DispatcherState::Normal { .. })); // polls: manual shutdown assert_eq!(h1.poll_count, 2); - if let DispatcherState::Normal(ref inner) = h1.inner { + if let DispatcherState::Normal { ref inner } = h1.inner { let io = inner.io.as_ref().unwrap(); let mut res = (&io.write_buf()[..]).to_owned(); stabilize_date_header(&mut res); @@ -1386,7 +1422,7 @@ mod tests { actix_rt::pin!(h1); assert!(h1.as_mut().poll(cx).is_ready()); - assert!(matches!(&h1.inner, DispatcherState::Upgrade(_))); + assert!(matches!(&h1.inner, DispatcherState::Upgrade { .. })); // polls: manual shutdown assert_eq!(h1.poll_count, 2); From 57ea322ce5acefba4f17cb090d9dc5b7da4c1de1 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Fri, 17 Dec 2021 22:09:08 +0300 Subject: [PATCH 39/87] simplify MessageBody::complete_body interface (#2522) --- actix-http/src/body/boxed.rs | 57 ++++-- actix-http/src/body/either.rs | 18 +- actix-http/src/body/message_body.rs | 289 +++++++--------------------- actix-http/src/body/none.rs | 9 +- actix-http/src/encoding/encoder.rs | 54 +++--- actix-http/src/h1/dispatcher.rs | 6 +- src/dev.rs | 8 + 7 files changed, 149 insertions(+), 292 deletions(-) diff --git a/actix-http/src/body/boxed.rs b/actix-http/src/body/boxed.rs index 7581bec88..a2d7540c4 100644 --- a/actix-http/src/body/boxed.rs +++ b/actix-http/src/body/boxed.rs @@ -8,10 +8,16 @@ use std::{ use bytes::Bytes; use super::{BodySize, MessageBody, MessageBodyMapErr}; -use crate::Error; +use crate::body; /// A boxed message body with boxed errors. -pub struct BoxBody(Pin>>>); +pub struct BoxBody(BoxBodyInner); + +enum BoxBodyInner { + None(body::None), + Bytes(Bytes), + Stream(Pin>>>), +} impl BoxBody { /// Same as `MessageBody::boxed`. @@ -23,29 +29,42 @@ impl BoxBody { where B: MessageBody + 'static, { - let body = MessageBodyMapErr::new(body, Into::into); - Self(Box::pin(body)) + match body.size() { + BodySize::None => Self(BoxBodyInner::None(body::None)), + _ => match body.try_into_bytes() { + Ok(bytes) => Self(BoxBodyInner::Bytes(bytes)), + Err(body) => { + let body = MessageBodyMapErr::new(body, Into::into); + Self(BoxBodyInner::Stream(Box::pin(body))) + } + }, + } } /// Returns a mutable pinned reference to the inner message body type. #[inline] - pub fn as_pin_mut(&mut self) -> Pin<&mut (dyn MessageBody>)> { - self.0.as_mut() + pub fn as_pin_mut(&mut self) -> Pin<&mut Self> { + Pin::new(self) } } impl fmt::Debug for BoxBody { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // TODO show BoxBodyInner f.write_str("BoxBody(dyn MessageBody)") } } impl MessageBody for BoxBody { - type Error = Error; + type Error = Box; #[inline] fn size(&self) -> BodySize { - self.0.size() + match &self.0 { + BoxBodyInner::None(none) => none.size(), + BoxBodyInner::Bytes(bytes) => bytes.size(), + BoxBodyInner::Stream(stream) => stream.size(), + } } #[inline] @@ -53,20 +72,20 @@ impl MessageBody for BoxBody { mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - self.0 - .as_mut() - .poll_next(cx) - .map_err(|err| Error::new_body().with_cause(err)) + match &mut self.0 { + BoxBodyInner::None(_) => Poll::Ready(None), + BoxBodyInner::Bytes(bytes) => Pin::new(bytes).poll_next(cx).map_err(Into::into), + BoxBodyInner::Stream(stream) => Pin::new(stream).poll_next(cx), + } } #[inline] - fn is_complete_body(&self) -> bool { - self.0.is_complete_body() - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - self.0.take_complete_body() + fn try_into_bytes(self) -> Result { + match self.0 { + BoxBodyInner::None(none) => Ok(none.try_into_bytes().unwrap()), + BoxBodyInner::Bytes(bytes) => Ok(bytes.try_into_bytes().unwrap()), + _ => Err(self), + } } #[inline] diff --git a/actix-http/src/body/either.rs b/actix-http/src/body/either.rs index 3a4082dc9..add1eab7c 100644 --- a/actix-http/src/body/either.rs +++ b/actix-http/src/body/either.rs @@ -74,18 +74,14 @@ where } #[inline] - fn is_complete_body(&self) -> bool { + fn try_into_bytes(self) -> Result { match self { - EitherBody::Left { body } => body.is_complete_body(), - EitherBody::Right { body } => body.is_complete_body(), - } - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - match self { - EitherBody::Left { body } => body.take_complete_body(), - EitherBody::Right { body } => body.take_complete_body(), + EitherBody::Left { body } => body + .try_into_bytes() + .map_err(|body| EitherBody::Left { body }), + EitherBody::Right { body } => body + .try_into_bytes() + .map_err(|body| EitherBody::Right { body }), } } diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index 075ae7220..bd13e75ec 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -31,51 +31,14 @@ pub trait MessageBody { cx: &mut Context<'_>, ) -> Poll>>; - /// Returns true if entire body bytes chunk is obtainable in one call to `poll_next`. + /// Convert this body into `Bytes`. /// - /// This method's implementation should agree with [`take_complete_body`] and should always be - /// checked before taking the body. - /// - /// The default implementation returns `false. - /// - /// [`take_complete_body`]: MessageBody::take_complete_body - fn is_complete_body(&self) -> bool { - false - } - - /// Returns the complete chunk of body bytes. - /// - /// Implementors of this method should note the following: - /// - It is acceptable to skip the omit checks of [`is_complete_body`]. The responsibility of - /// performing this check is delegated to the caller. - /// - If the result of [`is_complete_body`] is conditional, that condition should be given - /// equivalent attention here. - /// - A second call call to [`take_complete_body`] should return an empty `Bytes` or panic. - /// - A call to [`poll_next`] after calling [`take_complete_body`] should return `None` unless - /// the chunk is guaranteed to be empty. - /// - /// The default implementation panics unconditionally, indicating a control flow bug in the - /// calling code. - /// - /// # Panics - /// With a correct implementation, panics if called without first checking [`is_complete_body`]. - /// - /// [`is_complete_body`]: MessageBody::is_complete_body - /// [`take_complete_body`]: MessageBody::take_complete_body - /// [`poll_next`]: MessageBody::poll_next - fn take_complete_body(&mut self) -> Bytes { - assert!( - self.is_complete_body(), - "type ({}) allows taking complete body but did not provide an implementation \ - of `take_complete_body`", - std::any::type_name::() - ); - - unimplemented!( - "type ({}) does not allow taking complete body; caller should make sure to \ - check `is_complete_body` first", - std::any::type_name::() - ); + /// Bodies with `BodySize::None` are allowed to return empty `Bytes`. + fn try_into_bytes(self) -> Result + where + Self: Sized, + { + Err(self) } /// Converts this body into `BoxBody`. @@ -104,14 +67,6 @@ mod foreign_impls { ) -> Poll>> { match *self {} } - - fn is_complete_body(&self) -> bool { - true - } - - fn take_complete_body(&mut self) -> Bytes { - match *self {} - } } impl MessageBody for () { @@ -131,13 +86,8 @@ mod foreign_impls { } #[inline] - fn is_complete_body(&self) -> bool { - true - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - Bytes::new() + fn try_into_bytes(self) -> Result { + Ok(Bytes::new()) } } @@ -159,16 +109,6 @@ mod foreign_impls { ) -> Poll>> { Pin::new(self.get_mut().as_mut()).poll_next(cx) } - - #[inline] - fn is_complete_body(&self) -> bool { - self.as_ref().is_complete_body() - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - self.as_mut().take_complete_body() - } } impl MessageBody for Pin> @@ -189,38 +129,6 @@ mod foreign_impls { ) -> Poll>> { self.get_mut().as_mut().poll_next(cx) } - - #[inline] - fn is_complete_body(&self) -> bool { - self.as_ref().is_complete_body() - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - debug_assert!( - self.is_complete_body(), - "inner type \"{}\" does not allow taking complete body; caller should make sure to \ - call `is_complete_body` first", - std::any::type_name::(), - ); - - // we do not have DerefMut access to call take_complete_body directly but since - // is_complete_body is true we should expect the entire bytes chunk in one poll_next - - let waker = futures_task::noop_waker(); - let mut cx = Context::from_waker(&waker); - - match self.as_mut().poll_next(&mut cx) { - Poll::Ready(Some(Ok(data))) => data, - _ => { - panic!( - "inner type \"{}\" indicated it allows taking complete body but failed to \ - return Bytes when polled", - std::any::type_name::() - ); - } - } - } } impl MessageBody for &'static [u8] { @@ -232,24 +140,19 @@ mod foreign_impls { } fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(self.take_complete_body()))) + Poll::Ready(Some(Ok(Bytes::from_static(mem::take(self.get_mut()))))) } } #[inline] - fn is_complete_body(&self) -> bool { - true - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - Bytes::from_static(mem::take(self)) + fn try_into_bytes(self) -> Result { + Ok(Bytes::from_static(self)) } } @@ -262,24 +165,19 @@ mod foreign_impls { } fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(self.take_complete_body()))) + Poll::Ready(Some(Ok(mem::take(self.get_mut())))) } } #[inline] - fn is_complete_body(&self) -> bool { - true - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - mem::take(self) + fn try_into_bytes(self) -> Result { + Ok(self) } } @@ -292,24 +190,19 @@ mod foreign_impls { } fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(self.take_complete_body()))) + Poll::Ready(Some(Ok(mem::take(self.get_mut()).freeze()))) } } #[inline] - fn is_complete_body(&self) -> bool { - true - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - mem::take(self).freeze() + fn try_into_bytes(self) -> Result { + Ok(self.freeze()) } } @@ -322,24 +215,19 @@ mod foreign_impls { } fn poll_next( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll>> { if self.is_empty() { Poll::Ready(None) } else { - Poll::Ready(Some(Ok(self.take_complete_body()))) + Poll::Ready(Some(Ok(mem::take(self.get_mut()).into()))) } } #[inline] - fn is_complete_body(&self) -> bool { - true - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - Bytes::from(mem::take(self)) + fn try_into_bytes(self) -> Result { + Ok(Bytes::from(self)) } } @@ -365,13 +253,8 @@ mod foreign_impls { } #[inline] - fn is_complete_body(&self) -> bool { - true - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - Bytes::from_static(mem::take(self).as_bytes()) + fn try_into_bytes(self) -> Result { + Ok(Bytes::from_static(self.as_bytes())) } } @@ -396,13 +279,8 @@ mod foreign_impls { } #[inline] - fn is_complete_body(&self) -> bool { - true - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - Bytes::from(mem::take(self)) + fn try_into_bytes(self) -> Result { + Ok(Bytes::from(self)) } } @@ -423,13 +301,8 @@ mod foreign_impls { } #[inline] - fn is_complete_body(&self) -> bool { - true - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - mem::take(self).into_bytes() + fn try_into_bytes(self) -> Result { + Ok(self.into_bytes()) } } } @@ -486,13 +359,9 @@ where } #[inline] - fn is_complete_body(&self) -> bool { - self.body.is_complete_body() - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - self.body.take_complete_body() + fn try_into_bytes(self) -> Result { + let Self { body, mapper } = self; + body.try_into_bytes().map_err(|body| Self { body, mapper }) } } @@ -503,6 +372,7 @@ mod tests { use bytes::{Bytes, BytesMut}; use super::*; + use crate::body::{self, EitherBody}; macro_rules! assert_poll_next { ($pin:expr, $exp:expr) => { @@ -604,70 +474,45 @@ mod tests { assert_poll_next!(pl, Bytes::from("test")); } - #[test] - fn take_string() { - let mut data = "test".repeat(2); - let data_bytes = Bytes::from(data.clone()); - assert!(data.is_complete_body()); - assert_eq!(data.take_complete_body(), data_bytes); - - let mut big_data = "test".repeat(64 * 1024); - let data_bytes = Bytes::from(big_data.clone()); - assert!(big_data.is_complete_body()); - assert_eq!(big_data.take_complete_body(), data_bytes); - } - - #[test] - fn take_boxed_equivalence() { - let mut data = Bytes::from_static(b"test"); - assert!(data.is_complete_body()); - assert_eq!(data.take_complete_body(), b"test".as_ref()); - - let mut data = Box::new(Bytes::from_static(b"test")); - assert!(data.is_complete_body()); - assert_eq!(data.take_complete_body(), b"test".as_ref()); - - let mut data = Box::pin(Bytes::from_static(b"test")); - assert!(data.is_complete_body()); - assert_eq!(data.take_complete_body(), b"test".as_ref()); - } - - #[test] - fn take_policy() { - let mut data = Bytes::from_static(b"test"); - // first call returns chunk - assert_eq!(data.take_complete_body(), b"test".as_ref()); - // second call returns empty - assert_eq!(data.take_complete_body(), b"".as_ref()); - - let waker = futures_task::noop_waker(); - let mut cx = Context::from_waker(&waker); - let mut data = Bytes::from_static(b"test"); - // take returns whole chunk - assert_eq!(data.take_complete_body(), b"test".as_ref()); - // subsequent poll_next returns None - assert_eq!(Pin::new(&mut data).poll_next(&mut cx), Poll::Ready(None)); - } - - #[test] - fn complete_body_combinators() { - use crate::body::{BoxBody, EitherBody}; - + #[actix_rt::test] + async fn complete_body_combinators() { + let body = Bytes::from_static(b"test"); + let body = BoxBody::new(body); + let body = EitherBody::<_, ()>::left(body); + let body = EitherBody::<(), _>::right(body); + // Do not support try_into_bytes: + // let body = Box::new(body); + // let body = Box::pin(body); + + assert_eq!(body.try_into_bytes().unwrap(), Bytes::from("test")); + } + + #[actix_rt::test] + async fn complete_body_combinators_poll() { let body = Bytes::from_static(b"test"); let body = BoxBody::new(body); let body = EitherBody::<_, ()>::left(body); let body = EitherBody::<(), _>::right(body); - let body = Box::new(body); - let body = Box::pin(body); let mut body = body; - assert!(body.is_complete_body()); - assert_eq!(body.take_complete_body(), b"test".as_ref()); + assert_eq!(body.size(), BodySize::Sized(4)); + assert_poll_next!(Pin::new(&mut body), Bytes::from("test")); + assert_poll_next_none!(Pin::new(&mut body)); + } - // subsequent poll_next returns None - let waker = futures_task::noop_waker(); - let mut cx = Context::from_waker(&waker); - assert!(Pin::new(&mut body).poll_next(&mut cx).map_err(drop) == Poll::Ready(None)); + #[actix_rt::test] + async fn none_body_combinators() { + fn none_body() -> BoxBody { + let body = body::None; + let body = BoxBody::new(body); + let body = EitherBody::<_, ()>::left(body); + let body = EitherBody::<(), _>::right(body); + body.boxed() + } + + assert_eq!(none_body().size(), BodySize::None); + assert_eq!(none_body().try_into_bytes().unwrap(), Bytes::new()); + assert_poll_next_none!(Pin::new(&mut none_body())); } // down-casting used to be done with a method on MessageBody trait diff --git a/actix-http/src/body/none.rs b/actix-http/src/body/none.rs index bb494078f..0e7bbe5a9 100644 --- a/actix-http/src/body/none.rs +++ b/actix-http/src/body/none.rs @@ -42,12 +42,7 @@ impl MessageBody for None { } #[inline] - fn is_complete_body(&self) -> bool { - true - } - - #[inline] - fn take_complete_body(&mut self) -> Bytes { - Bytes::new() + fn try_into_bytes(self) -> Result { + Ok(Bytes::new()) } } diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index fa294ab0d..70448a115 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -53,7 +53,7 @@ impl Encoder { } } - pub fn response(encoding: ContentEncoding, head: &mut ResponseHead, mut body: B) -> Self { + pub fn response(encoding: ContentEncoding, head: &mut ResponseHead, body: B) -> Self { let can_encode = !(head.headers().contains_key(&CONTENT_ENCODING) || head.status == StatusCode::SWITCHING_PROTOCOLS || head.status == StatusCode::NO_CONTENT @@ -65,11 +65,9 @@ impl Encoder { return Self::none(); } - let body = if body.is_complete_body() { - let body = body.take_complete_body(); - EncoderBody::Full { body } - } else { - EncoderBody::Stream { body } + let body = match body.try_into_bytes() { + Ok(body) => EncoderBody::Full { body }, + Err(body) => EncoderBody::Stream { body }, }; if can_encode { @@ -133,21 +131,14 @@ where } } - fn is_complete_body(&self) -> bool { + fn try_into_bytes(self) -> Result + where + Self: Sized, + { match self { - EncoderBody::None => true, - EncoderBody::Full { .. } => true, - EncoderBody::Stream { .. } => false, - } - } - - fn take_complete_body(&mut self) -> Bytes { - match self { - EncoderBody::None => Bytes::new(), - EncoderBody::Full { body } => body.take_complete_body(), - EncoderBody::Stream { .. } => { - panic!("EncoderBody::Stream variant cannot be taken") - } + EncoderBody::None => Ok(Bytes::new()), + EncoderBody::Full { body } => Ok(body), + _ => Err(self), } } } @@ -234,19 +225,20 @@ where } } - fn is_complete_body(&self) -> bool { + fn try_into_bytes(mut self) -> Result + where + Self: Sized, + { if self.encoder.is_some() { - false + Err(self) } else { - self.body.is_complete_body() - } - } - - fn take_complete_body(&mut self) -> Bytes { - if self.encoder.is_some() { - panic!("compressed body stream cannot be taken") - } else { - self.body.take_complete_body() + match self.body.try_into_bytes() { + Ok(body) => Ok(body), + Err(body) => { + self.body = body; + Err(self) + } + } } } } diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 472845e65..5c0cb64af 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -22,7 +22,7 @@ use crate::{ config::ServiceConfig, error::{DispatchError, ParseError, PayloadError}, service::HttpFlow, - Extensions, OnConnectData, Request, Response, StatusCode, + Error, Extensions, OnConnectData, Request, Response, StatusCode, }; use super::{ @@ -494,7 +494,9 @@ where } Poll::Ready(Some(Err(err))) => { - return Err(DispatchError::Body(err.into())) + return Err(DispatchError::Body( + Error::new_body().with_cause(err).into(), + )) } Poll::Pending => return Ok(PollResponse::DoNothing), diff --git a/src/dev.rs b/src/dev.rs index d4a64985c..edcc158f8 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -139,4 +139,12 @@ impl crate::body::MessageBody for AnyBody { AnyBody::Boxed { body } => body.as_pin_mut().poll_next(cx), } } + + fn try_into_bytes(self) -> Result { + match self { + AnyBody::None => Ok(crate::web::Bytes::new()), + AnyBody::Full { body } => Ok(body), + _ => Err(self), + } + } } From aa31086af5ce0c67eeeec3fc8858cc35770d8a40 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Dec 2021 19:16:42 +0000 Subject: [PATCH 40/87] improve BoxBody Debug impl --- actix-http/src/body/boxed.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/actix-http/src/body/boxed.rs b/actix-http/src/body/boxed.rs index a2d7540c4..0c6950a1f 100644 --- a/actix-http/src/body/boxed.rs +++ b/actix-http/src/body/boxed.rs @@ -11,6 +11,7 @@ use super::{BodySize, MessageBody, MessageBodyMapErr}; use crate::body; /// A boxed message body with boxed errors. +#[derive(Debug)] pub struct BoxBody(BoxBodyInner); enum BoxBodyInner { @@ -19,6 +20,16 @@ enum BoxBodyInner { Stream(Pin>>>), } +impl fmt::Debug for BoxBodyInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::None(arg0) => f.debug_tuple("None").field(arg0).finish(), + Self::Bytes(arg0) => f.debug_tuple("Bytes").field(arg0).finish(), + Self::Stream(_) => f.debug_tuple("Stream").field(&"dyn MessageBody").finish(), + } + } +} + impl BoxBody { /// Same as `MessageBody::boxed`. /// @@ -48,13 +59,6 @@ impl BoxBody { } } -impl fmt::Debug for BoxBody { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // TODO show BoxBodyInner - f.write_str("BoxBody(dyn MessageBody)") - } -} - impl MessageBody for BoxBody { type Error = Box; From 1d6f5ba6d696515d8b8eb71f0e353b2fc9a797b8 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Dec 2021 19:19:21 +0000 Subject: [PATCH 41/87] improve codegen on BoxBody poll_next --- actix-http/src/body/boxed.rs | 14 +++++++++----- actix-http/src/body/message_body.rs | 10 +++++++++- actix-http/src/encoding/encoder.rs | 4 ++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/actix-http/src/body/boxed.rs b/actix-http/src/body/boxed.rs index 0c6950a1f..d109a6a74 100644 --- a/actix-http/src/body/boxed.rs +++ b/actix-http/src/body/boxed.rs @@ -77,17 +77,21 @@ impl MessageBody for BoxBody { cx: &mut Context<'_>, ) -> Poll>> { match &mut self.0 { - BoxBodyInner::None(_) => Poll::Ready(None), - BoxBodyInner::Bytes(bytes) => Pin::new(bytes).poll_next(cx).map_err(Into::into), - BoxBodyInner::Stream(stream) => Pin::new(stream).poll_next(cx), + BoxBodyInner::None(body) => { + Pin::new(body).poll_next(cx).map_err(|err| match err {}) + } + BoxBodyInner::Bytes(body) => { + Pin::new(body).poll_next(cx).map_err(|err| match err {}) + } + BoxBodyInner::Stream(body) => Pin::new(body).poll_next(cx), } } #[inline] fn try_into_bytes(self) -> Result { match self.0 { - BoxBodyInner::None(none) => Ok(none.try_into_bytes().unwrap()), - BoxBodyInner::Bytes(bytes) => Ok(bytes.try_into_bytes().unwrap()), + BoxBodyInner::None(body) => Ok(body.try_into_bytes().unwrap()), + BoxBodyInner::Bytes(body) => Ok(body.try_into_bytes().unwrap()), _ => Err(self), } } diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index bd13e75ec..cf65bac2d 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -33,7 +33,8 @@ pub trait MessageBody { /// Convert this body into `Bytes`. /// - /// Bodies with `BodySize::None` are allowed to return empty `Bytes`. + /// Body types with `BodySize::None` are allowed to return empty `Bytes`. + #[inline] fn try_into_bytes(self) -> Result where Self: Sized, @@ -139,6 +140,7 @@ mod foreign_impls { BodySize::Sized(self.len() as u64) } + #[inline] fn poll_next( self: Pin<&mut Self>, _cx: &mut Context<'_>, @@ -164,6 +166,7 @@ mod foreign_impls { BodySize::Sized(self.len() as u64) } + #[inline] fn poll_next( self: Pin<&mut Self>, _cx: &mut Context<'_>, @@ -189,6 +192,7 @@ mod foreign_impls { BodySize::Sized(self.len() as u64) } + #[inline] fn poll_next( self: Pin<&mut Self>, _cx: &mut Context<'_>, @@ -214,6 +218,7 @@ mod foreign_impls { BodySize::Sized(self.len() as u64) } + #[inline] fn poll_next( self: Pin<&mut Self>, _cx: &mut Context<'_>, @@ -239,6 +244,7 @@ mod foreign_impls { BodySize::Sized(self.len() as u64) } + #[inline] fn poll_next( self: Pin<&mut Self>, _cx: &mut Context<'_>, @@ -266,6 +272,7 @@ mod foreign_impls { BodySize::Sized(self.len() as u64) } + #[inline] fn poll_next( self: Pin<&mut Self>, _cx: &mut Context<'_>, @@ -292,6 +299,7 @@ mod foreign_impls { BodySize::Sized(self.len() as u64) } + #[inline] fn poll_next( self: Pin<&mut Self>, _cx: &mut Context<'_>, diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index 70448a115..d06d6b4d8 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -108,6 +108,7 @@ where { type Error = EncoderError; + #[inline] fn size(&self) -> BodySize { match self { EncoderBody::None => BodySize::None, @@ -131,6 +132,7 @@ where } } + #[inline] fn try_into_bytes(self) -> Result where Self: Sized, @@ -149,6 +151,7 @@ where { type Error = EncoderError; + #[inline] fn size(&self) -> BodySize { if self.encoder.is_some() { BodySize::Stream @@ -225,6 +228,7 @@ where } } + #[inline] fn try_into_bytes(mut self) -> Result where Self: Sized, From 5842a3279daff2a34bdf1151d317ebbe813288c7 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Dec 2021 19:35:08 +0000 Subject: [PATCH 42/87] update messagebody documentation --- actix-http/CHANGES.md | 9 ++++++++- actix-http/src/body/message_body.rs | 20 ++++++++++++++++---- actix-http/src/response.rs | 2 +- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 598ef9c0e..806e32cc0 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,12 +1,19 @@ # Changes ## Unreleased - 2021-xx-xx +### Added +* New method on `MessageBody` trait, `try_into_bytes`, with default implementation, for optimizations on body types that complete in exactly one poll. Replaces `is_complete_body` and `take_complete_body`. [#2522] + ### Changed * Rename trait `IntoHeaderPair => TryIntoHeaderPair`. [#2510] * Rename `TryIntoHeaderPair::{try_into_header_pair => try_into_pair}`. [#2510] * Rename trait `IntoHeaderValue => TryIntoHeaderValue`. [#2510] +### Removed +* `MessageBody::{is_complete_body,take_complete_body}`. [#2522] + [#2510]: https://github.com/actix/actix-web/pull/2510 +[#2522]: https://github.com/actix/actix-web/pull/2522 ## 3.0.0-beta.15 - 2021-12-11 @@ -27,7 +34,7 @@ * `Request::take_conn_data()`. [#2491] * `Request::take_req_data()`. [#2487] * `impl Clone` for `RequestHead`. [#2487] -* New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimisations on body types that are done in exactly one poll/chunk. [#2497] +* New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimizations on body types that are done in exactly one poll/chunk. [#2497] * New `boxed` method on `MessageBody` trait for wrapping body type. [#2520] ### Changed diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index cf65bac2d..0a605a69a 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -17,11 +17,15 @@ use super::{BodySize, BoxBody}; /// An interface types that can converted to bytes and used as response bodies. // TODO: examples pub trait MessageBody { - // TODO: consider this bound to only fmt::Display since the error type is not really used - // and there is an impl for Into> on String + /// The type of error that will be returned if streaming body fails. + /// + /// Since it is not appropriate to generate a response mid-stream, it only requires `Error` for + /// internal use and logging. type Error: Into>; /// Body size hint. + /// + /// If [`BodySize::None`] is returned, optimizations that skip reading the body are allowed. fn size(&self) -> BodySize; /// Attempt to pull out the next chunk of body bytes. @@ -31,9 +35,17 @@ pub trait MessageBody { cx: &mut Context<'_>, ) -> Poll>>; - /// Convert this body into `Bytes`. + /// Try to convert into the complete chunk of body bytes. /// - /// Body types with `BodySize::None` are allowed to return empty `Bytes`. + /// Implement this method if the entire body can be trivially extracted. This is useful for + /// optimizations where `poll_next` calls can be avoided. + /// + /// Body types with [`BodySize::None`] are allowed to return empty `Bytes`. Although, if calling + /// this method, it is recommended to check `size` first and return early. + /// + /// # Errors + /// The default implementation will error and return the original type back to the caller for + /// further use. #[inline] fn try_into_bytes(self) -> Result where diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index aee9e80b4..a0e6d9b7c 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -170,7 +170,7 @@ impl Response { /// Returns split head and body. /// /// # Implementation Notes - /// Due to internal performance optimisations, the first element of the returned tuple is a + /// 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. pub fn into_parts(self) -> (Response<()>, B) { self.replace_body(()) From ae47d96fc6831d6f27a05fb05e47c464fe4f45e1 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Dec 2021 20:56:54 +0000 Subject: [PATCH 43/87] use body::None in encoder body --- actix-http/src/encoding/encoder.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/actix-http/src/encoding/encoder.rs b/actix-http/src/encoding/encoder.rs index d06d6b4d8..b565bb2b5 100644 --- a/actix-http/src/encoding/encoder.rs +++ b/actix-http/src/encoding/encoder.rs @@ -25,7 +25,7 @@ use zstd::stream::write::Encoder as ZstdEncoder; use super::Writer; use crate::{ - body::{BodySize, MessageBody}, + body::{self, BodySize, MessageBody}, error::BlockingError, header::{self, ContentEncoding, HeaderValue, CONTENT_ENCODING}, ResponseHead, StatusCode, @@ -46,7 +46,9 @@ pin_project! { impl Encoder { fn none() -> Self { Encoder { - body: EncoderBody::None, + body: EncoderBody::None { + body: body::None::new(), + }, encoder: None, fut: None, eof: true, @@ -96,7 +98,7 @@ impl Encoder { pin_project! { #[project = EncoderBodyProj] enum EncoderBody { - None, + None { body: body::None }, Full { body: Bytes }, Stream { #[pin] body: B }, } @@ -111,7 +113,7 @@ where #[inline] fn size(&self) -> BodySize { match self { - EncoderBody::None => BodySize::None, + EncoderBody::None { body } => body.size(), EncoderBody::Full { body } => body.size(), EncoderBody::Stream { body } => body.size(), } @@ -122,7 +124,9 @@ where cx: &mut Context<'_>, ) -> Poll>> { match self.project() { - EncoderBodyProj::None => Poll::Ready(None), + EncoderBodyProj::None { body } => { + Pin::new(body).poll_next(cx).map_err(|err| match err {}) + } EncoderBodyProj::Full { body } => { Pin::new(body).poll_next(cx).map_err(|err| match err {}) } @@ -138,8 +142,8 @@ where Self: Sized, { match self { - EncoderBody::None => Ok(Bytes::new()), - EncoderBody::Full { body } => Ok(body), + EncoderBody::None { body } => Ok(body.try_into_bytes().unwrap()), + EncoderBody::Full { body } => Ok(body.try_into_bytes().unwrap()), _ => Err(self), } } From 7bf47967cc77090dad5dd247af55bd00c7808260 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Dec 2021 20:57:51 +0000 Subject: [PATCH 44/87] prepare actix-http release 3.0.0-beta.16 --- Cargo.toml | 2 +- actix-files/Cargo.toml | 2 +- actix-http-test/Cargo.toml | 2 +- actix-http/CHANGES.md | 3 +++ actix-http/Cargo.toml | 2 +- actix-http/README.md | 4 ++-- actix-multipart/Cargo.toml | 2 +- actix-test/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- awc/Cargo.toml | 4 ++-- 10 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e20529e1a..020fb03b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,7 +77,7 @@ actix-service = "2.0.0" actix-utils = "3.0.0" actix-tls = { version = "3.0.0-rc.1", default-features = false, optional = true } -actix-http = "3.0.0-beta.15" +actix-http = "3.0.0-beta.16" actix-router = "0.5.0-beta.2" actix-web-codegen = "0.5.0-beta.6" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index edb7cfab4..792f479d0 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -22,7 +22,7 @@ path = "src/lib.rs" experimental-io-uring = ["actix-web/experimental-io-uring", "tokio-uring"] [dependencies] -actix-http = "3.0.0-beta.15" +actix-http = "3.0.0-beta.16" actix-service = "2" actix-utils = "3" actix-web = { version = "4.0.0-beta.14", default-features = false } diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index 449fa342e..841a9a241 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -52,4 +52,4 @@ tokio = { version = "1.2", features = ["sync"] } [dev-dependencies] actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["cookies"] } -actix-http = "3.0.0-beta.15" +actix-http = "3.0.0-beta.16" diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 806e32cc0..218ed5a6e 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 3.0.0-beta.16 - 2021-12-17 ### Added * New method on `MessageBody` trait, `try_into_bytes`, with default implementation, for optimizations on body types that complete in exactly one poll. Replaces `is_complete_body` and `take_complete_body`. [#2522] diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 515574ab1..03ee1409e 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "3.0.0-beta.15" +version = "3.0.0-beta.16" authors = ["Nikolay Kim "] description = "HTTP primitives for the Actix ecosystem" keywords = ["actix", "http", "framework", "async", "futures"] diff --git a/actix-http/README.md b/actix-http/README.md index a2aa41333..731d7a48e 100644 --- a/actix-http/README.md +++ b/actix-http/README.md @@ -3,11 +3,11 @@ > HTTP primitives for the Actix ecosystem. [![crates.io](https://img.shields.io/crates/v/actix-http?label=latest)](https://crates.io/crates/actix-http) -[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.0.0-beta.15)](https://docs.rs/actix-http/3.0.0-beta.15) +[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.0.0-beta.16)](https://docs.rs/actix-http/3.0.0-beta.16) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg)
-[![dependency status](https://deps.rs/crate/actix-http/3.0.0-beta.15/status.svg)](https://deps.rs/crate/actix-http/3.0.0-beta.15) +[![dependency status](https://deps.rs/crate/actix-http/3.0.0-beta.16/status.svg)](https://deps.rs/crate/actix-http/3.0.0-beta.16) [![Download](https://img.shields.io/crates/d/actix-http.svg)](https://crates.io/crates/actix-http) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 6fd1211d9..8c3c86f08 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -28,7 +28,7 @@ twoway = "0.2" [dev-dependencies] actix-rt = "2.2" -actix-http = "3.0.0-beta.15" +actix-http = "3.0.0-beta.16" futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] } tokio = { version = "1", features = ["sync"] } tokio-stream = "0.1" diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index 71f99f791..67a0a087d 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -29,7 +29,7 @@ openssl = ["tls-openssl", "actix-http/openssl", "awc/openssl"] [dependencies] actix-codec = "0.4.1" -actix-http = "3.0.0-beta.15" +actix-http = "3.0.0-beta.16" actix-http-test = "3.0.0-beta.9" actix-rt = "2.1" actix-service = "2.0.0" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 128d68c15..e8145ee82 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -16,7 +16,7 @@ path = "src/lib.rs" [dependencies] actix = { version = "0.12.0", default-features = false } actix-codec = "0.4.1" -actix-http = "3.0.0-beta.15" +actix-http = "3.0.0-beta.16" actix-web = { version = "4.0.0-beta.14", default-features = false } bytes = "1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 60a95871c..4b1e00dde 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -60,7 +60,7 @@ dangerous-h2c = [] [dependencies] actix-codec = "0.4.1" actix-service = "2.0.0" -actix-http = "3.0.0-beta.15" +actix-http = "3.0.0-beta.16" actix-rt = { version = "2.1", default-features = false } actix-tls = { version = "3.0.0-rc.1", features = ["connect", "uri"] } actix-utils = "3.0.0" @@ -93,7 +93,7 @@ tls-rustls = { package = "rustls", version = "0.20.0", optional = true, features trust-dns-resolver = { version = "0.20.0", optional = true } [dev-dependencies] -actix-http = { version = "3.0.0-beta.15", features = ["openssl"] } +actix-http = { version = "3.0.0-beta.16", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.9", features = ["openssl"] } actix-server = "2.0.0-rc.1" actix-test = { version = "0.1.0-beta.8", features = ["openssl", "rustls"] } From 6c2c7b68e2d6eb5ee1c5cf879e8874928e420e72 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Dec 2021 20:59:01 +0000 Subject: [PATCH 45/87] prepare actix-web release 4.0.0-beta.15 --- CHANGES.md | 3 +++ Cargo.toml | 2 +- README.md | 4 ++-- actix-files/Cargo.toml | 4 ++-- actix-http-test/Cargo.toml | 2 +- actix-http/Cargo.toml | 2 +- actix-multipart/Cargo.toml | 2 +- actix-test/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- actix-web-codegen/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- 11 files changed, 15 insertions(+), 12 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6494ba4f6..1c0691efe 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 4.0.0-beta.15 - 2021-12-17 ### Added * Method on `Responder` trait (`customize`) for customizing responders and `CustomizeResponder` struct. [#2510] * Implement `Debug` for `DefaultHeaders`. [#2510] diff --git a/Cargo.toml b/Cargo.toml index 020fb03b5..3c4ddd1b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "4.0.0-beta.14" +version = "4.0.0-beta.15" authors = ["Nikolay Kim "] description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust" keywords = ["actix", "http", "web", "framework", "async"] diff --git a/README.md b/README.md index 4a1671905..5cce9f3b9 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@

[![crates.io](https://img.shields.io/crates/v/actix-web?label=latest)](https://crates.io/crates/actix-web) -[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.0.0-beta.14)](https://docs.rs/actix-web/4.0.0-beta.14) +[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.0.0-beta.15)](https://docs.rs/actix-web/4.0.0-beta.15) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg) -[![Dependency Status](https://deps.rs/crate/actix-web/4.0.0-beta.14/status.svg)](https://deps.rs/crate/actix-web/4.0.0-beta.14) +[![Dependency Status](https://deps.rs/crate/actix-web/4.0.0-beta.15/status.svg)](https://deps.rs/crate/actix-web/4.0.0-beta.15)
[![build status](https://github.com/actix/actix-web/workflows/CI%20%28Linux%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-web/actions) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 792f479d0..3e7c377bf 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -25,7 +25,7 @@ experimental-io-uring = ["actix-web/experimental-io-uring", "tokio-uring"] actix-http = "3.0.0-beta.16" actix-service = "2" actix-utils = "3" -actix-web = { version = "4.0.0-beta.14", default-features = false } +actix-web = { version = "4.0.0-beta.15", default-features = false } askama_escape = "0.10" bitflags = "1" @@ -44,4 +44,4 @@ tokio-uring = { version = "0.1", optional = true } [dev-dependencies] actix-rt = "2.2" actix-test = "0.1.0-beta.8" -actix-web = "4.0.0-beta.14" +actix-web = "4.0.0-beta.15" diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index 841a9a241..70855b5e6 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -51,5 +51,5 @@ tls-openssl = { version = "0.10.9", package = "openssl", optional = true } tokio = { version = "1.2", features = ["sync"] } [dev-dependencies] -actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["cookies"] } +actix-web = { version = "4.0.0-beta.15", default-features = false, features = ["cookies"] } actix-http = "3.0.0-beta.16" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 03ee1409e..9f93bf6d2 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -83,7 +83,7 @@ zstd = { version = "0.9", optional = true } actix-http-test = { version = "3.0.0-beta.9", features = ["openssl"] } actix-server = "2.0.0-rc.1" actix-tls = { version = "3.0.0-rc.1", features = ["openssl"] } -actix-web = "4.0.0-beta.14" +actix-web = "4.0.0-beta.15" async-stream = "0.3" criterion = { version = "0.3", features = ["html_reports"] } diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 8c3c86f08..c7145e542 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -15,7 +15,7 @@ path = "src/lib.rs" [dependencies] actix-utils = "3.0.0" -actix-web = { version = "4.0.0-beta.14", default-features = false } +actix-web = { version = "4.0.0-beta.15", default-features = false } bytes = "1" derive_more = "0.99.5" diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index 67a0a087d..c2a7a3211 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -34,7 +34,7 @@ actix-http-test = "3.0.0-beta.9" actix-rt = "2.1" actix-service = "2.0.0" actix-utils = "3.0.0" -actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["cookies"] } +actix-web = { version = "4.0.0-beta.15", default-features = false, features = ["cookies"] } awc = { version = "3.0.0-beta.13", default-features = false, features = ["cookies"] } futures-core = { version = "0.3.7", default-features = false, features = ["std"] } diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index e8145ee82..a2a69153d 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" actix = { version = "0.12.0", default-features = false } actix-codec = "0.4.1" actix-http = "3.0.0-beta.16" -actix-web = { version = "4.0.0-beta.14", default-features = false } +actix-web = { version = "4.0.0-beta.15", default-features = false } bytes = "1" bytestring = "1" diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 211f19da6..6571e2a24 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -25,7 +25,7 @@ actix-macros = "0.2.3" actix-rt = "2.2" actix-test = "0.1.0-beta.8" actix-utils = "3.0.0" -actix-web = "4.0.0-beta.14" +actix-web = "4.0.0-beta.15" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } trybuild = "1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 4b1e00dde..9ca6acb75 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -99,7 +99,7 @@ actix-server = "2.0.0-rc.1" actix-test = { version = "0.1.0-beta.8", features = ["openssl", "rustls"] } actix-tls = { version = "3.0.0-rc.1", features = ["openssl", "rustls"] } actix-utils = "3.0.0" -actix-web = { version = "4.0.0-beta.14", features = ["openssl"] } +actix-web = { version = "4.0.0-beta.15", features = ["openssl"] } brotli2 = "0.3.2" env_logger = "0.9" From 8340b63b7b18009b006671ff9f1fbb790d67bf19 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Dec 2021 20:59:59 +0000 Subject: [PATCH 46/87] prepare awc release 3.0.0-beta.14 --- Cargo.toml | 2 +- actix-http-test/Cargo.toml | 2 +- actix-test/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- awc/CHANGES.md | 3 +++ awc/Cargo.toml | 2 +- awc/README.md | 4 ++-- 7 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3c4ddd1b0..c46211821 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,7 +107,7 @@ url = "2.1" [dev-dependencies] actix-test = { version = "0.1.0-beta.8", features = ["openssl", "rustls"] } -awc = { version = "3.0.0-beta.13", features = ["openssl"] } +awc = { version = "3.0.0-beta.14", features = ["openssl"] } brotli2 = "0.3.2" criterion = { version = "0.3", features = ["html_reports"] } diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index 70855b5e6..0c205fc2a 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -35,7 +35,7 @@ actix-tls = "3.0.0-rc.1" actix-utils = "3.0.0" actix-rt = "2.2" actix-server = "2.0.0-rc.1" -awc = { version = "3.0.0-beta.13", default-features = false } +awc = { version = "3.0.0-beta.14", default-features = false } base64 = "0.13" bytes = "1" diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index c2a7a3211..9f409019b 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -35,7 +35,7 @@ actix-rt = "2.1" actix-service = "2.0.0" actix-utils = "3.0.0" actix-web = { version = "4.0.0-beta.15", default-features = false, features = ["cookies"] } -awc = { version = "3.0.0-beta.13", default-features = false, features = ["cookies"] } +awc = { version = "3.0.0-beta.14", default-features = false, features = ["cookies"] } futures-core = { version = "0.3.7", default-features = false, features = ["std"] } futures-util = { version = "0.3.7", default-features = false, features = [] } diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index a2a69153d..6c45a5479 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -28,7 +28,7 @@ tokio = { version = "1", features = ["sync"] } [dev-dependencies] actix-rt = "2.2" actix-test = "0.1.0-beta.8" -awc = { version = "3.0.0-beta.13", default-features = false } +awc = { version = "3.0.0-beta.14", default-features = false } env_logger = "0.9" futures-util = { version = "0.3.7", default-features = false } diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 8a3fea46a..ef0f9faaa 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 3.0.0-beta.14 - 2021-12-17 * Add `ClientBuilder::add_default_header` and deprecate `ClientBuilder::header`. [#2510] [#2510]: https://github.com/actix/actix-web/pull/2510 diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 9ca6acb75..dc9e503b1 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "3.0.0-beta.13" +version = "3.0.0-beta.14" authors = [ "Nikolay Kim ", "fakeshadow <24548779@qq.com>", diff --git a/awc/README.md b/awc/README.md index f3c5452fc..3fbdd903a 100644 --- a/awc/README.md +++ b/awc/README.md @@ -3,9 +3,9 @@ > Async HTTP and WebSocket client library. [![crates.io](https://img.shields.io/crates/v/awc?label=latest)](https://crates.io/crates/awc) -[![Documentation](https://docs.rs/awc/badge.svg?version=3.0.0-beta.13)](https://docs.rs/awc/3.0.0-beta.13) +[![Documentation](https://docs.rs/awc/badge.svg?version=3.0.0-beta.14)](https://docs.rs/awc/3.0.0-beta.14) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/awc) -[![Dependency Status](https://deps.rs/crate/awc/3.0.0-beta.13/status.svg)](https://deps.rs/crate/awc/3.0.0-beta.13) +[![Dependency Status](https://deps.rs/crate/awc/3.0.0-beta.14/status.svg)](https://deps.rs/crate/awc/3.0.0-beta.14) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) ## Documentation & Resources From 73bbe56971bff3cdcf8e6c0098351c4daf76b74e Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Dec 2021 21:00:15 +0000 Subject: [PATCH 47/87] prepare actix-test release 0.1.0-beta.9 --- Cargo.toml | 2 +- actix-files/Cargo.toml | 2 +- actix-test/CHANGES.md | 3 +++ actix-test/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- actix-web-codegen/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- 7 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c46211821..af6b5909a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,7 +106,7 @@ time = { version = "0.3", default-features = false, features = ["formatting"] } url = "2.1" [dev-dependencies] -actix-test = { version = "0.1.0-beta.8", features = ["openssl", "rustls"] } +actix-test = { version = "0.1.0-beta.9", features = ["openssl", "rustls"] } awc = { version = "3.0.0-beta.14", features = ["openssl"] } brotli2 = "0.3.2" diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 3e7c377bf..c74a8e9a6 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -43,5 +43,5 @@ tokio-uring = { version = "0.1", optional = true } [dev-dependencies] actix-rt = "2.2" -actix-test = "0.1.0-beta.8" +actix-test = "0.1.0-beta.9" actix-web = "4.0.0-beta.15" diff --git a/actix-test/CHANGES.md b/actix-test/CHANGES.md index b7107b44f..ef78ac54a 100644 --- a/actix-test/CHANGES.md +++ b/actix-test/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 0.1.0-beta.9 - 2021-12-17 * Re-export `actix_http::body::to_bytes`. [#2518] * Update `actix_web::test` re-exports. [#2518] diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index 9f409019b..7957b3a9c 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-test" -version = "0.1.0-beta.8" +version = "0.1.0-beta.9" authors = [ "Nikolay Kim ", "Rob Ede ", diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 6c45a5479..3f213f378 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -27,7 +27,7 @@ tokio = { version = "1", features = ["sync"] } [dev-dependencies] actix-rt = "2.2" -actix-test = "0.1.0-beta.8" +actix-test = "0.1.0-beta.9" awc = { version = "3.0.0-beta.14", default-features = false } env_logger = "0.9" diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 6571e2a24..502483599 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -23,7 +23,7 @@ actix-router = "0.5.0-beta.2" [dev-dependencies] actix-macros = "0.2.3" actix-rt = "2.2" -actix-test = "0.1.0-beta.8" +actix-test = "0.1.0-beta.9" actix-utils = "3.0.0" actix-web = "4.0.0-beta.15" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index dc9e503b1..4cab20d05 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -96,7 +96,7 @@ trust-dns-resolver = { version = "0.20.0", optional = true } actix-http = { version = "3.0.0-beta.16", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.9", features = ["openssl"] } actix-server = "2.0.0-rc.1" -actix-test = { version = "0.1.0-beta.8", features = ["openssl", "rustls"] } +actix-test = { version = "0.1.0-beta.9", features = ["openssl", "rustls"] } actix-tls = { version = "3.0.0-rc.1", features = ["openssl", "rustls"] } actix-utils = "3.0.0" actix-web = { version = "4.0.0-beta.15", features = ["openssl"] } From 9cd8526085b5aa4538d17495f8e21313d7c53527 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Dec 2021 21:23:00 +0000 Subject: [PATCH 48/87] prepare actix-router release 0.5.0-beta.3 --- Cargo.toml | 2 +- actix-router/CHANGES.md | 3 +++ actix-router/Cargo.toml | 2 +- actix-web-codegen/Cargo.toml | 2 +- scripts/unreleased | 41 ++++++++++++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 3 deletions(-) create mode 100755 scripts/unreleased diff --git a/Cargo.toml b/Cargo.toml index af6b5909a..02bef3af6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,7 @@ actix-utils = "3.0.0" actix-tls = { version = "3.0.0-rc.1", default-features = false, optional = true } actix-http = "3.0.0-beta.16" -actix-router = "0.5.0-beta.2" +actix-router = "0.5.0-beta.3" actix-web-codegen = "0.5.0-beta.6" ahash = "0.7" diff --git a/actix-router/CHANGES.md b/actix-router/CHANGES.md index c2858f2ba..d0ed55c88 100644 --- a/actix-router/CHANGES.md +++ b/actix-router/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 0.5.0-beta.3 - 2021-12-17 * Minimum supported Rust version (MSRV) is now 1.52. diff --git a/actix-router/Cargo.toml b/actix-router/Cargo.toml index b95bca505..afd39dfd3 100644 --- a/actix-router/Cargo.toml +++ b/actix-router/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-router" -version = "0.5.0-beta.2" +version = "0.5.0-beta.3" authors = [ "Nikolay Kim ", "Ali MJ Al-Nasrawy ", diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 502483599..8d42137e7 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -18,7 +18,7 @@ proc-macro = true quote = "1" syn = { version = "1", features = ["full", "parsing"] } proc-macro2 = "1" -actix-router = "0.5.0-beta.2" +actix-router = "0.5.0-beta.3" [dev-dependencies] actix-macros = "0.2.3" diff --git a/scripts/unreleased b/scripts/unreleased new file mode 100755 index 000000000..4dfa2d9ae --- /dev/null +++ b/scripts/unreleased @@ -0,0 +1,41 @@ +#!/bin/sh + +set -euo pipefail + +bold="\033[1m" +reset="\033[0m" + +unreleased_for() { + DIR=$1 + + CARGO_MANIFEST=$DIR/Cargo.toml + CHANGELOG_FILE=$DIR/CHANGES.md + + # get current version + PACKAGE_NAME="$(sed -nE 's/^name ?= ?"([^"]+)"$/\1/ p' "$CARGO_MANIFEST" | head -n 1)" + CURRENT_VERSION="$(sed -nE 's/^version ?= ?"([^"]+)"$/\1/ p' "$CARGO_MANIFEST")" + + CHANGE_CHUNK_FILE="$(mktemp)" + + # get changelog chunk and save to temp file + cat "$CHANGELOG_FILE" | + # skip up to unreleased heading + sed '1,/Unreleased/ d' | + # take up to previous version heading + sed "/$CURRENT_VERSION/ q" | + # drop last line + sed '$d' \ + >"$CHANGE_CHUNK_FILE" + + # if word count of changelog chunk is 0 then exit + if [ "$(wc -w "$CHANGE_CHUNK_FILE" | awk '{ print $1 }')" = "0" ]; then + return 0; + fi + + echo "${bold}# ${PACKAGE_NAME}${reset} since ${bold}v$CURRENT_VERSION${reset}" + cat "$CHANGE_CHUNK_FILE" +} + +for f in $(fd --absolute-path CHANGES.md); do + unreleased_for $(dirname $f) +done From 0bd5ccc43285ff871c1ea908a74e1b1d9e0f1409 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 17 Dec 2021 21:39:15 +0000 Subject: [PATCH 49/87] update changelog --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 1c0691efe..857974d3f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,7 +15,7 @@ * Rename `test::{default_service => simple_service}`. Old name is deprecated. [#2518] * Rename `test::{read_response_json => call_and_read_body_json}`. Old name is deprecated. [#2518] * Rename `test::{read_response => call_and_read_body}`. Old name is deprecated. [#2518] -* Relax body type and error bounds on test utilities. +* Relax body type and error bounds on test utilities. [#2518] ### Removed * Top-level `EitherExtractError` export. [#2510] From 84ea9e7e8849d98108a66990f8508fc014d78d19 Mon Sep 17 00:00:00 2001 From: Thales Date: Fri, 17 Dec 2021 21:05:12 -0300 Subject: [PATCH 50/87] http: Replace header::map::GetAll with std::slice::Iter (#2527) --- actix-http/CHANGES.md | 4 +++ actix-http/src/header/map.rs | 55 ++++-------------------------------- src/response/response.rs | 2 +- 3 files changed, 11 insertions(+), 50 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 218ed5a6e..c5e57e1a4 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,6 +1,10 @@ # Changes ## Unreleased - 2021-xx-xx +### Removed +* `header::map::GetAll` iterator, its `Iterator::size_hint` method was wrongly implemented. Replaced with `std::slice::Iter`. [#2527] + +[#2527]: https://github.com/actix/actix-web/pull/2527 ## 3.0.0-beta.16 - 2021-12-17 diff --git a/actix-http/src/header/map.rs b/actix-http/src/header/map.rs index 748410375..478867ed0 100644 --- a/actix-http/src/header/map.rs +++ b/actix-http/src/header/map.rs @@ -306,8 +306,11 @@ impl HeaderMap { /// assert_eq!(set_cookies_iter.next().unwrap(), "two=2"); /// assert!(set_cookies_iter.next().is_none()); /// ``` - pub fn get_all(&self, key: impl AsHeaderName) -> GetAll<'_> { - GetAll::new(self.get_value(key)) + pub fn get_all(&self, key: impl AsHeaderName) -> std::slice::Iter<'_, HeaderValue> { + match self.get_value(key) { + Some(value) => value.iter(), + None => (&[]).iter(), + } } // TODO: get_all_mut ? @@ -602,52 +605,6 @@ impl<'a> IntoIterator for &'a HeaderMap { } } -/// Iterator over borrowed values with the same associated name. -/// -/// See [`HeaderMap::get_all`]. -#[derive(Debug)] -pub struct GetAll<'a> { - idx: usize, - value: Option<&'a Value>, -} - -impl<'a> GetAll<'a> { - fn new(value: Option<&'a Value>) -> Self { - Self { idx: 0, value } - } -} - -impl<'a> Iterator for GetAll<'a> { - type Item = &'a HeaderValue; - - fn next(&mut self) -> Option { - let val = self.value?; - - match val.get(self.idx) { - Some(val) => { - self.idx += 1; - Some(val) - } - None => { - // current index is none; remove value to fast-path future next calls - self.value = None; - None - } - } - } - - fn size_hint(&self) -> (usize, Option) { - match self.value { - Some(val) => (val.len(), Some(val.len())), - None => (0, Some(0)), - } - } -} - -impl ExactSizeIterator for GetAll<'_> {} - -impl iter::FusedIterator for GetAll<'_> {} - /// Iterator over removed, owned values with the same associated name. /// /// Returned from methods that remove or replace items. See [`HeaderMap::insert`] @@ -895,7 +852,7 @@ mod tests { assert_impl_all!(HeaderMap: IntoIterator); assert_impl_all!(Keys<'_>: Iterator, ExactSizeIterator, FusedIterator); - assert_impl_all!(GetAll<'_>: Iterator, ExactSizeIterator, FusedIterator); + assert_impl_all!(std::slice::Iter<'_, HeaderValue>: Iterator, ExactSizeIterator, FusedIterator); assert_impl_all!(Removed: Iterator, ExactSizeIterator, FusedIterator); assert_impl_all!(Iter<'_>: Iterator, ExactSizeIterator, FusedIterator); assert_impl_all!(IntoIter: Iterator, ExactSizeIterator, FusedIterator); diff --git a/src/response/response.rs b/src/response/response.rs index 4fb4b44b6..6fa2082e7 100644 --- a/src/response/response.rs +++ b/src/response/response.rs @@ -313,7 +313,7 @@ impl Future for HttpResponse { #[cfg(feature = "cookies")] pub struct CookieIter<'a> { - iter: header::map::GetAll<'a>, + iter: std::slice::Iter<'a, HeaderValue>, } #[cfg(feature = "cookies")] From 5c53db1e4ddb37e1e81ff5bb20e86d1ec87d6a44 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 18 Dec 2021 01:48:16 +0000 Subject: [PATCH 51/87] remove hidden anybody --- src/dev.rs | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) diff --git a/src/dev.rs b/src/dev.rs index edcc158f8..23a40f292 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -102,49 +102,3 @@ impl BodyEncoding for crate::HttpResponse { self } } - -// TODO: remove this if it doesn't appear to be needed - -#[allow(dead_code)] -#[derive(Debug)] -pub(crate) enum AnyBody { - None, - Full { body: crate::web::Bytes }, - Boxed { body: actix_http::body::BoxBody }, -} - -impl crate::body::MessageBody for AnyBody { - type Error = crate::BoxError; - - /// Body size hint. - fn size(&self) -> crate::body::BodySize { - match self { - AnyBody::None => crate::body::BodySize::None, - AnyBody::Full { body } => body.size(), - AnyBody::Boxed { body } => body.size(), - } - } - - /// Attempt to pull out the next chunk of body bytes. - fn poll_next( - self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll>> { - match self.get_mut() { - AnyBody::None => std::task::Poll::Ready(None), - AnyBody::Full { body } => { - let bytes = std::mem::take(body); - std::task::Poll::Ready(Some(Ok(bytes))) - } - AnyBody::Boxed { body } => body.as_pin_mut().poll_next(cx), - } - } - - fn try_into_bytes(self) -> Result { - match self { - AnyBody::None => Ok(crate::web::Bytes::new()), - AnyBody::Full { body } => Ok(body), - _ => Err(self), - } - } -} From d2b97240109b0f09e55900ad645c3cf6d5102956 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 18 Dec 2021 03:27:32 +0000 Subject: [PATCH 52/87] update bump script to detect prerelease versions --- scripts/bump | 14 ++++++++++++-- src/app.rs | 18 ++++++++++-------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/scripts/bump b/scripts/bump index 0c360569d..43cd8b8c7 100755 --- a/scripts/bump +++ b/scripts/bump @@ -55,6 +55,11 @@ else read -p "Update version to: " NEW_VERSION fi +# strip leading v from input +if [ "${NEW_VERSION:0:1}" = "v" ]; then + NEW_VERSION="${NEW_VERSION:1}" +fi + DATE="$(date -u +"%Y-%m-%d")" echo "updating from $CURRENT_VERSION => $NEW_VERSION ($DATE)" @@ -124,15 +129,20 @@ SHORT_PACKAGE_NAME="$(echo $PACKAGE_NAME | sed 's/^actix-web-//' | sed 's/^actix GIT_TAG="$(echo $SHORT_PACKAGE_NAME-v$NEW_VERSION)" RELEASE_TITLE="$(echo $PACKAGE_NAME: v$NEW_VERSION)" +if [ "$(echo $NEW_VERSION | grep beta)" ] || [ "$(echo $NEW_VERSION | grep rc)" ] || [ "$(echo $NEW_VERSION | grep alpha)" ]; then + PRERELEASE="--prerelease" +fi + echo echo "GitHub release command:" -echo "gh release create \"$GIT_TAG\" --draft --title \"$RELEASE_TITLE\" --notes-file \"$CHANGE_CHUNK_FILE\" --prerelease" +GH_CMD="gh release create \"$GIT_TAG\" --draft --title \"$RELEASE_TITLE\" --notes-file \"$CHANGE_CHUNK_FILE\" ${PRERELEASE:-}" +echo "$GH_CMD" read -p "Submit draft GH release: (y/N) " GH_RELEASE GH_RELEASE="${GH_RELEASE:-n}" if [ "$GH_RELEASE" = 'y' ] || [ "$GH_RELEASE" = 'Y' ]; then - gh release create "$GIT_TAG" --draft --title "$RELEASE_TITLE" --notes-file "$CHANGE_CHUNK_FILE" --prerelease + eval "$GH_CMD" fi echo diff --git a/src/app.rs b/src/app.rs index feb35d7ae..6bccc1ff1 100644 --- a/src/app.rs +++ b/src/app.rs @@ -486,19 +486,21 @@ where #[cfg(test)] mod tests { - use actix_service::Service; + use actix_service::Service as _; use actix_utils::future::{err, ok}; use bytes::Bytes; use super::*; - use crate::http::{ - header::{self, HeaderValue}, - Method, StatusCode, + use crate::{ + http::{ + header::{self, HeaderValue}, + Method, StatusCode, + }, + middleware::DefaultHeaders, + service::ServiceRequest, + test::{call_service, init_service, read_body, try_init_service, TestRequest}, + web, HttpRequest, HttpResponse, }; - use crate::middleware::DefaultHeaders; - use crate::service::ServiceRequest; - use crate::test::{call_service, init_service, read_body, try_init_service, TestRequest}; - use crate::{web, HttpRequest, HttpResponse}; #[actix_rt::test] async fn test_default_resource() { From fb036264cc4ccaa9dd380b40f8b548a3353a7c36 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 18 Dec 2021 16:44:30 +0000 Subject: [PATCH 53/87] only build `SslConnectorBuilder` once (#2503) --- actix-http-test/src/lib.rs | 2 +- actix-test/src/lib.rs | 2 +- awc/CHANGES.md | 4 +++ awc/Cargo.toml | 2 +- awc/src/client/connector.rs | 69 ++++++++++++++++++++++++++---------- awc/tests/test_connector.rs | 2 +- awc/tests/test_ssl_client.rs | 2 +- tests/test_httpserver.rs | 2 +- 8 files changed, 61 insertions(+), 24 deletions(-) diff --git a/actix-http-test/src/lib.rs b/actix-http-test/src/lib.rs index e7e479ab2..03239ece6 100644 --- a/actix-http-test/src/lib.rs +++ b/actix-http-test/src/lib.rs @@ -107,7 +107,7 @@ pub async fn test_server_with_addr>( Connector::new() .conn_lifetime(Duration::from_secs(0)) .timeout(Duration::from_millis(30000)) - .ssl(builder.build()) + .openssl(builder.build()) }; #[cfg(not(feature = "openssl"))] diff --git a/actix-test/src/lib.rs b/actix-test/src/lib.rs index 3808ba69a..f86120f2f 100644 --- a/actix-test/src/lib.rs +++ b/actix-test/src/lib.rs @@ -340,7 +340,7 @@ where Connector::new() .conn_lifetime(Duration::from_secs(0)) .timeout(Duration::from_millis(30000)) - .ssl(builder.build()) + .openssl(builder.build()) } #[cfg(not(feature = "openssl"))] { diff --git a/awc/CHANGES.md b/awc/CHANGES.md index ef0f9faaa..7b822930c 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,6 +1,10 @@ # Changes ## Unreleased - 2021-xx-xx +* Rename `Connector::{ssl => openssl}`. [#2503] +* Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503] + +[#2503]: https://github.com/actix/actix-web/pull/2503 ## 3.0.0-beta.14 - 2021-12-17 diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 4cab20d05..f9a541c7e 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -62,7 +62,7 @@ actix-codec = "0.4.1" actix-service = "2.0.0" actix-http = "3.0.0-beta.16" actix-rt = { version = "2.1", default-features = false } -actix-tls = { version = "3.0.0-rc.1", features = ["connect", "uri"] } +actix-tls = { version = "3.0.0-rc.2", features = ["connect", "uri"] } actix-utils = "3.0.0" ahash = "0.7" diff --git a/awc/src/client/connector.rs b/awc/src/client/connector.rs index 40b3c4d32..423f656a8 100644 --- a/awc/src/client/connector.rs +++ b/awc/src/client/connector.rs @@ -22,11 +22,13 @@ use futures_core::{future::LocalBoxFuture, ready}; use http::Uri; use pin_project_lite::pin_project; -use super::config::ConnectorConfig; -use super::connection::{Connection, ConnectionIo}; -use super::error::ConnectError; -use super::pool::ConnectionPool; -use super::Connect; +use super::{ + config::ConnectorConfig, + connection::{Connection, ConnectionIo}, + error::ConnectError, + pool::ConnectionPool, + Connect, +}; enum OurTlsConnector { #[allow(dead_code)] // only dead when no TLS feature is enabled @@ -35,6 +37,12 @@ enum OurTlsConnector { #[cfg(feature = "openssl")] Openssl(actix_tls::connect::openssl::reexports::SslConnector), + /// Provided because building the OpenSSL context on newer versions can be very slow. + /// This prevents unnecessary calls to `.build()` while constructing the client connector. + #[cfg(feature = "openssl")] + #[allow(dead_code)] // false positive; used in build_ssl + OpensslBuilder(actix_tls::connect::openssl::reexports::SslConnectorBuilder), + #[cfg(feature = "rustls")] Rustls(std::sync::Arc), } @@ -57,7 +65,7 @@ pub struct Connector { config: ConnectorConfig, #[allow(dead_code)] // only dead when no TLS feature is enabled - ssl: OurTlsConnector, + tls: OurTlsConnector, } impl Connector<()> { @@ -72,7 +80,7 @@ impl Connector<()> { Connector { connector: TcpConnector::new(resolver::resolver()).service(), config: ConnectorConfig::default(), - ssl: Self::build_ssl(vec![b"h2".to_vec(), b"http/1.1".to_vec()]), + tls: Self::build_ssl(vec![b"h2".to_vec(), b"http/1.1".to_vec()]), } } @@ -116,7 +124,7 @@ impl Connector<()> { log::error!("Can not set ALPN protocol: {:?}", err); } - OurTlsConnector::Openssl(ssl.build()) + OurTlsConnector::OpensslBuilder(ssl) } } @@ -134,7 +142,7 @@ impl Connector { Connector { connector, config: self.config, - ssl: self.ssl, + tls: self.tls, } } } @@ -167,23 +175,35 @@ where self } + /// Use custom OpenSSL `SslConnector` instance. #[cfg(feature = "openssl")] - /// Use custom `SslConnector` instance. + pub fn openssl( + mut self, + connector: actix_tls::connect::openssl::reexports::SslConnector, + ) -> Self { + self.tls = OurTlsConnector::Openssl(connector); + self + } + + /// See docs for [`Connector::openssl`]. + #[doc(hidden)] + #[cfg(feature = "openssl")] + #[deprecated(since = "3.0.0", note = "Renamed to `Connector::openssl`.")] pub fn ssl( mut self, connector: actix_tls::connect::openssl::reexports::SslConnector, ) -> Self { - self.ssl = OurTlsConnector::Openssl(connector); + self.tls = OurTlsConnector::Openssl(connector); self } + /// Use custom Rustls `ClientConfig` instance. #[cfg(feature = "rustls")] - /// Use custom `ClientConfig` instance. pub fn rustls( mut self, connector: std::sync::Arc, ) -> Self { - self.ssl = OurTlsConnector::Rustls(connector); + self.tls = OurTlsConnector::Rustls(connector); self } @@ -198,7 +218,7 @@ where unimplemented!("actix-http client only supports versions http/1.1 & http/2") } }; - self.ssl = Connector::build_ssl(versions); + self.tls = Connector::build_ssl(versions); self } @@ -270,8 +290,8 @@ where } /// Finish configuration process and create connector service. - /// The Connector builder always concludes by calling `finish()` last in - /// its combinator chain. + /// + /// The `Connector` builder always concludes by calling `finish()` last in its combinator chain. pub fn finish(self) -> ConnectorService { let local_address = self.config.local_address; let timeout = self.config.timeout; @@ -284,7 +304,15 @@ where service: tcp_service_inner.clone(), }; - let tls_service = match self.ssl { + let tls = match self.tls { + #[cfg(feature = "openssl")] + OurTlsConnector::OpensslBuilder(builder) => { + OurTlsConnector::Openssl(builder.build()) + } + tls => tls, + }; + + let tls_service = match tls { OurTlsConnector::None => { #[cfg(not(feature = "dangerous-h2c"))] { @@ -374,6 +402,11 @@ where Some(actix_service::boxed::rc_service(tls_service)) } + #[cfg(feature = "openssl")] + OurTlsConnector::OpensslBuilder(_) => { + unreachable!("OpenSSL builder is built before this match."); + } + #[cfg(feature = "rustls")] OurTlsConnector::Rustls(tls) => { const H2: &[u8] = b"h2"; @@ -853,7 +886,7 @@ mod tests { let connector = Connector { connector: TcpConnector::new(resolver::resolver()).service(), config: ConnectorConfig::default(), - ssl: OurTlsConnector::None, + tls: OurTlsConnector::None, }; let client = Client::builder().connector(connector).finish(); diff --git a/awc/tests/test_connector.rs b/awc/tests/test_connector.rs index 588c51463..0f0b81414 100644 --- a/awc/tests/test_connector.rs +++ b/awc/tests/test_connector.rs @@ -58,7 +58,7 @@ async fn test_connection_window_size() { .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); let client = awc::Client::builder() - .connector(awc::Connector::new().ssl(builder.build())) + .connector(awc::Connector::new().openssl(builder.build())) .initial_window_size(100) .initial_connection_window_size(100) .finish(); diff --git a/awc/tests/test_ssl_client.rs b/awc/tests/test_ssl_client.rs index 811efd4bc..40c9ab8f0 100644 --- a/awc/tests/test_ssl_client.rs +++ b/awc/tests/test_ssl_client.rs @@ -72,7 +72,7 @@ async fn test_connection_reuse_h2() { .map_err(|e| log::error!("Can not set alpn protocol: {:?}", e)); let client = awc::Client::builder() - .connector(awc::Connector::new().ssl(builder.build())) + .connector(awc::Connector::new().openssl(builder.build())) .finish(); // req 1 diff --git a/tests/test_httpserver.rs b/tests/test_httpserver.rs index 887b51d41..464a650a2 100644 --- a/tests/test_httpserver.rs +++ b/tests/test_httpserver.rs @@ -121,7 +121,7 @@ async fn test_start_ssl() { let client = awc::Client::builder() .connector( awc::Connector::new() - .ssl(builder.build()) + .openssl(builder.build()) .timeout(Duration::from_millis(100)), ) .finish(); From 7d507a41ee9af8351d9dc028c48aadea627dadaa Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 19 Dec 2021 03:58:29 +0000 Subject: [PATCH 54/87] Create FUNDING.yml --- .github/FUNDING.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..39e5d30cc --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: [robjtede] +open_collective: actix From 2e00776d5e650376cc0a8d3de4bf8a964a404c68 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 19 Dec 2021 04:18:57 +0000 Subject: [PATCH 55/87] Update FUNDING.yml --- .github/FUNDING.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 39e5d30cc..6164c657c 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,4 +1,3 @@ # These are supported funding model platforms github: [robjtede] -open_collective: actix From 17f636a1839850b0141ac0b697e8a74129f8a512 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 19 Dec 2021 17:05:27 +0000 Subject: [PATCH 56/87] split request and response modules (#2530) --- actix-http/CHANGES.md | 4 +- actix-http/src/h1/client.rs | 16 +- actix-http/src/h1/codec.rs | 18 +- actix-http/src/h1/decoder.rs | 23 +- actix-http/src/h1/encoder.rs | 22 +- actix-http/src/h1/expect.rs | 3 +- actix-http/src/h1/mod.rs | 2 +- actix-http/src/h1/upgrade.rs | 4 +- actix-http/src/h1/utils.rs | 3 +- actix-http/src/lib.rs | 20 +- actix-http/src/message.rs | 382 +----------------- actix-http/src/payload.rs | 1 + actix-http/src/requests/head.rs | 174 ++++++++ actix-http/src/requests/mod.rs | 7 + actix-http/src/{ => requests}/request.rs | 8 +- .../builder.rs} | 4 +- actix-http/src/responses/head.rs | 208 ++++++++++ actix-http/src/responses/mod.rs | 11 + actix-http/src/{ => responses}/response.rs | 4 +- actix-http/src/ws/mod.rs | 2 +- src/app_service.rs | 1 + 21 files changed, 467 insertions(+), 450 deletions(-) create mode 100644 actix-http/src/requests/head.rs create mode 100644 actix-http/src/requests/mod.rs rename actix-http/src/{ => requests}/request.rs (97%) rename actix-http/src/{response_builder.rs => responses/builder.rs} (99%) create mode 100644 actix-http/src/responses/head.rs create mode 100644 actix-http/src/responses/mod.rs rename actix-http/src/{ => responses}/response.rs (99%) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index c5e57e1a4..ad98d132a 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,8 +1,8 @@ # Changes ## Unreleased - 2021-xx-xx -### Removed -* `header::map::GetAll` iterator, its `Iterator::size_hint` method was wrongly implemented. Replaced with `std::slice::Iter`. [#2527] +### Changes +* `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527] [#2527]: https://github.com/actix/actix-web/pull/2527 diff --git a/actix-http/src/h1/client.rs b/actix-http/src/h1/client.rs index bec167971..9bd896ae0 100644 --- a/actix-http/src/h1/client.rs +++ b/actix-http/src/h1/client.rs @@ -5,13 +5,15 @@ use bitflags::bitflags; use bytes::{Bytes, BytesMut}; use http::{Method, Version}; -use super::decoder::{PayloadDecoder, PayloadItem, PayloadType}; -use super::{decoder, encoder, reserve_readbuf}; -use super::{Message, MessageType}; -use crate::body::BodySize; -use crate::config::ServiceConfig; -use crate::error::{ParseError, PayloadError}; -use crate::message::{ConnectionType, RequestHeadType, ResponseHead}; +use super::{ + decoder::{self, PayloadDecoder, PayloadItem, PayloadType}, + encoder, reserve_readbuf, Message, MessageType, +}; +use crate::{ + body::BodySize, + error::{ParseError, PayloadError}, + ConnectionType, RequestHeadType, ResponseHead, ServiceConfig, +}; bitflags! { struct Flags: u8 { diff --git a/actix-http/src/h1/codec.rs b/actix-http/src/h1/codec.rs index 29f6f4170..9a8907579 100644 --- a/actix-http/src/h1/codec.rs +++ b/actix-http/src/h1/codec.rs @@ -5,15 +5,13 @@ use bitflags::bitflags; use bytes::BytesMut; use http::{Method, Version}; -use super::decoder::{PayloadDecoder, PayloadItem, PayloadType}; -use super::{decoder, encoder}; -use super::{Message, MessageType}; -use crate::body::BodySize; -use crate::config::ServiceConfig; -use crate::error::ParseError; -use crate::message::ConnectionType; -use crate::request::Request; -use crate::response::Response; +use super::{ + decoder::{self, PayloadDecoder, PayloadItem, PayloadType}, + encoder, Message, MessageType, +}; +use crate::{ + body::BodySize, error::ParseError, ConnectionType, Request, Response, ServiceConfig, +}; bitflags! { struct Flags: u8 { @@ -199,7 +197,7 @@ mod tests { use http::Method; use super::*; - use crate::HttpMessage; + use crate::HttpMessage as _; #[actix_rt::test] async fn test_http_request_chunked_payload_and_next_message() { diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index eb142f844..3d3a3ceac 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -2,17 +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::{ConnectionType, ResponseHead}, - request::Request, -}; +use crate::{error::ParseError, header::HeaderMap, ConnectionType, Request, ResponseHead}; pub(crate) const MAX_BUFFER_SIZE: usize = 131_072; const MAX_HEADERS: usize = 96; @@ -50,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); @@ -193,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); } } @@ -278,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/encoder.rs b/actix-http/src/h1/encoder.rs index 49bf5432d..f2a862278 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -1,19 +1,19 @@ -use std::io::Write; -use std::marker::PhantomData; -use std::ptr::copy_nonoverlapping; -use std::slice::from_raw_parts_mut; -use std::{cmp, io}; +use std::{ + cmp, + io::{self, Write as _}, + marker::PhantomData, + ptr::copy_nonoverlapping, + slice::from_raw_parts_mut, +}; use bytes::{BufMut, BytesMut}; use crate::{ body::BodySize, - config::ServiceConfig, - header::{map::Value, HeaderMap, HeaderName}, - header::{CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING}, - helpers, - message::{ConnectionType, RequestHeadType}, - Response, StatusCode, Version, + header::{ + map::Value, HeaderMap, HeaderName, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING, + }, + helpers, ConnectionType, RequestHeadType, Response, ServiceConfig, StatusCode, Version, }; const AVERAGE_HEADER_SIZE: usize = 30; diff --git a/actix-http/src/h1/expect.rs b/actix-http/src/h1/expect.rs index bb8e28e95..bef281e59 100644 --- a/actix-http/src/h1/expect.rs +++ b/actix-http/src/h1/expect.rs @@ -1,8 +1,7 @@ use actix_service::{Service, ServiceFactory}; use actix_utils::future::{ready, Ready}; -use crate::error::Error; -use crate::request::Request; +use crate::{Error, Request}; pub struct ExpectHandler; diff --git a/actix-http/src/h1/mod.rs b/actix-http/src/h1/mod.rs index 17cbfb90f..64586a2dc 100644 --- a/actix-http/src/h1/mod.rs +++ b/actix-http/src/h1/mod.rs @@ -59,7 +59,7 @@ pub(crate) fn reserve_readbuf(src: &mut BytesMut) { #[cfg(test)] mod tests { use super::*; - use crate::request::Request; + use crate::Request; impl Message { pub fn message(self) -> Request { diff --git a/actix-http/src/h1/upgrade.rs b/actix-http/src/h1/upgrade.rs index e57ea8ae9..f25b0718b 100644 --- a/actix-http/src/h1/upgrade.rs +++ b/actix-http/src/h1/upgrade.rs @@ -2,9 +2,7 @@ use actix_codec::Framed; use actix_service::{Service, ServiceFactory}; use futures_core::future::LocalBoxFuture; -use crate::error::Error; -use crate::h1::Codec; -use crate::request::Request; +use crate::{h1::Codec, Error, Request}; pub struct UpgradeHandler; 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 60dc26f0f..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 request; -mod response; -mod response_builder; +mod requests; +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, RequestHead, RequestHeadType, ResponseHead}; +pub use self::message::Message; pub use self::payload::{Payload, PayloadStream}; -pub use self::request::Request; -pub use self::response::Response; -pub use self::response_builder::ResponseBuilder; +pub use self::requests::{Request, RequestHead, RequestHeadType}; +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 31c2db718..2692a4bee 100644 --- a/actix-http/src/message.rs +++ b/actix-http/src/message.rs @@ -1,16 +1,7 @@ -use std::{ - cell::{Ref, RefCell, RefMut}, - net, - rc::Rc, -}; +use std::{cell::RefCell, rc::Rc}; use bitflags::bitflags; -use crate::{ - header::{self, HeaderMap}, - Extensions, Method, StatusCode, Uri, Version, -}; - /// Represents various types of connection #[derive(Copy, Clone, PartialEq, Debug)] pub enum ConnectionType { @@ -44,294 +35,6 @@ pub trait Head: Default + 'static { F: FnOnce(&MessagePool) -> R; } -#[derive(Debug, Clone)] -pub struct RequestHead { - pub method: Method, - pub uri: Uri, - pub version: Version, - pub headers: HeaderMap, - pub peer_addr: Option, - 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: F) -> R - where - F: FnOnce(&MessagePool) -> 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, Option), -} - -impl RequestHeadType { - pub fn extra_headers(&self) -> Option<&HeaderMap> { - match self { - RequestHeadType::Owned(_) => None, - RequestHeadType::Rc(_, headers) => headers.as_ref(), - } - } -} - -impl AsRef for RequestHeadType { - fn as_ref(&self) -> &RequestHead { - match self { - RequestHeadType::Owned(head) => head, - RequestHeadType::Rc(head, _) => head.as_ref(), - } - } -} - -impl From 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, - 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. @@ -365,53 +68,12 @@ 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>>); -#[doc(hidden)] -#[allow(clippy::vec_box)] -/// Request's objects pool -pub struct BoxedResponsePool(RefCell>>); - -thread_local!(static REQUEST_POOL: MessagePool = MessagePool::::create()); -thread_local!(static RESPONSE_POOL: BoxedResponsePool = BoxedResponsePool::create()); - impl MessagePool { - fn create() -> MessagePool { + pub(crate) fn create() -> MessagePool { MessagePool(RefCell::new(Vec::with_capacity(128))) } @@ -433,43 +95,11 @@ impl MessagePool { } #[inline] - /// Release request instance + /// Release message instance fn release(&self, msg: Rc) { - let v = &mut self.0.borrow_mut(); - if v.len() < 128 { - v.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) { - let v = &mut self.0.borrow_mut(); - if v.len() < 128 { - msg.extensions.get_mut().clear(); - v.push(msg); + let pool = &mut self.0.borrow_mut(); + if pool.len() < 128 { + pool.push(msg); } } } diff --git a/actix-http/src/payload.rs b/actix-http/src/payload.rs index 5734af341..89a1a2db1 100644 --- a/actix-http/src/payload.rs +++ b/actix-http/src/payload.rs @@ -7,6 +7,7 @@ use h2::RecvStream; use crate::error::PayloadError; +// TODO: rename to boxed payload /// A boxed payload. pub type PayloadStream = Pin>>>; diff --git a/actix-http/src/requests/head.rs b/actix-http/src/requests/head.rs new file mode 100644 index 000000000..524075b61 --- /dev/null +++ b/actix-http/src/requests/head.rs @@ -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 = MessagePool::::create() +} + +#[derive(Debug, Clone)] +pub struct RequestHead { + pub method: Method, + pub uri: Uri, + pub version: Version, + pub headers: HeaderMap, + pub peer_addr: Option, + 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: F) -> R + where + F: FnOnce(&MessagePool) -> 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, Option), +} + +impl RequestHeadType { + pub fn extra_headers(&self) -> Option<&HeaderMap> { + match self { + RequestHeadType::Owned(_) => None, + RequestHeadType::Rc(_, headers) => headers.as_ref(), + } + } +} + +impl AsRef for RequestHeadType { + fn as_ref(&self) -> &RequestHead { + match self { + RequestHeadType::Owned(head) => head, + RequestHeadType::Rc(head, _) => head.as_ref(), + } + } +} + +impl From for RequestHeadType { + fn from(head: RequestHead) -> Self { + RequestHeadType::Owned(head) + } +} diff --git a/actix-http/src/requests/mod.rs b/actix-http/src/requests/mod.rs new file mode 100644 index 000000000..fc35da65a --- /dev/null +++ b/actix-http/src/requests/mod.rs @@ -0,0 +1,7 @@ +//! HTTP requests. + +mod head; +mod request; + +pub use self::head::{RequestHead, RequestHeadType}; +pub use self::request::Request; diff --git a/actix-http/src/request.rs b/actix-http/src/requests/request.rs similarity index 97% rename from actix-http/src/request.rs rename to actix-http/src/requests/request.rs index c7752d470..74347fbc2 100644 --- a/actix-http/src/request.rs +++ b/actix-http/src/requests/request.rs @@ -10,11 +10,7 @@ use std::{ use http::{header, Method, Uri, Version}; use crate::{ - extensions::Extensions, - header::HeaderMap, - message::{Message, RequestHead}, - payload::{Payload, PayloadStream}, - HttpMessage, + header::HeaderMap, Extensions, HttpMessage, Message, Payload, PayloadStream, RequestHead, }; /// An HTTP request. @@ -206,7 +202,7 @@ impl

Request

{ /// Returns the request data container, leaving an empty one in it's place. pub fn take_req_data(&mut self) -> Extensions { - mem::take(&mut self.req_data.get_mut()) + mem::take(self.req_data.get_mut()) } } 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. diff --git a/actix-http/src/ws/mod.rs b/actix-http/src/ws/mod.rs index c23d4edfc..6491da149 100644 --- a/actix-http/src/ws/mod.rs +++ b/actix-http/src/ws/mod.rs @@ -9,7 +9,7 @@ use derive_more::{Display, Error, From}; use http::{header, Method, StatusCode}; use crate::body::BoxBody; -use crate::{header::HeaderValue, message::RequestHead, response::Response, ResponseBuilder}; +use crate::{header::HeaderValue, RequestHead, Response, ResponseBuilder}; mod codec; mod dispatcher; diff --git a/src/app_service.rs b/src/app_service.rs index 515693db4..4e84cb201 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -221,6 +221,7 @@ where req_data, ) }; + self.service.call(ServiceRequest::new(req, payload)) } } From 64c2e5e1cd4c3c2315139fafa7f7e28b2ac6de07 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 22 Dec 2021 07:16:07 +0000 Subject: [PATCH 57/87] remove crate level clippy lint --- actix-http/src/builder.rs | 1 + actix-http/src/lib.rs | 7 +++---- actix-http/src/message.rs | 1 + actix-http/src/payload.rs | 6 ++++-- actix-http/src/requests/request.rs | 1 + actix-http/src/responses/response.rs | 5 ++--- 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/actix-http/src/builder.rs b/actix-http/src/builder.rs index 1b5da20b6..408ee7924 100644 --- a/actix-http/src/builder.rs +++ b/actix-http/src/builder.rs @@ -36,6 +36,7 @@ where >::Future: 'static, { /// Create instance of `ServiceConfigBuilder` + #[allow(clippy::new_without_default)] pub fn new() -> Self { HttpServiceBuilder { keep_alive: KeepAlive::Timeout(5), diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index ae822a055..2b7bc730b 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -19,7 +19,6 @@ #![allow( clippy::type_complexity, clippy::too_many_arguments, - clippy::new_without_default, clippy::borrow_interior_mutable_const )] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] @@ -28,6 +27,9 @@ #[macro_use] extern crate log; +pub use ::http::{uri, uri::Uri}; +pub use ::http::{Method, StatusCode, Version}; + pub mod body; mod builder; mod config; @@ -61,9 +63,6 @@ pub use self::requests::{Request, RequestHead, RequestHeadType}; pub use self::responses::{Response, ResponseBuilder, ResponseHead}; pub use self::service::HttpService; -pub use ::http::{uri, uri::Uri}; -pub use ::http::{Method, StatusCode, Version}; - /// A major HTTP protocol version. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] diff --git a/actix-http/src/message.rs b/actix-http/src/message.rs index 2692a4bee..34213f68a 100644 --- a/actix-http/src/message.rs +++ b/actix-http/src/message.rs @@ -43,6 +43,7 @@ pub struct Message { impl Message { /// Get new message from the pool of objects + #[allow(clippy::new_without_default)] pub fn new() -> Self { T::with_pool(MessagePool::get_message) } diff --git a/actix-http/src/payload.rs b/actix-http/src/payload.rs index 89a1a2db1..69840e7c1 100644 --- a/actix-http/src/payload.rs +++ b/actix-http/src/payload.rs @@ -1,5 +1,7 @@ -use std::pin::Pin; -use std::task::{Context, Poll}; +use std::{ + pin::Pin, + task::{Context, Poll}, +}; use bytes::Bytes; use futures_core::Stream; diff --git a/actix-http/src/requests/request.rs b/actix-http/src/requests/request.rs index 74347fbc2..0254a8f11 100644 --- a/actix-http/src/requests/request.rs +++ b/actix-http/src/requests/request.rs @@ -59,6 +59,7 @@ impl From> for Request { impl Request { /// Create new Request instance + #[allow(clippy::new_without_default)] pub fn new() -> Request { Request { head: Message::new(), diff --git a/actix-http/src/responses/response.rs b/actix-http/src/responses/response.rs index d781bdfaa..ec9157afb 100644 --- a/actix-http/src/responses/response.rs +++ b/actix-http/src/responses/response.rs @@ -10,10 +10,9 @@ use bytestring::ByteString; use crate::{ body::{BoxBody, MessageBody}, - extensions::Extensions, header::{self, HeaderMap, TryIntoHeaderValue}, - responses::{BoxedResponseHead, ResponseBuilder, ResponseHead}, - Error, StatusCode, + responses::BoxedResponseHead, + Error, Extensions, ResponseBuilder, ResponseHead, StatusCode, }; /// An HTTP response. From f8488aff1e348fca884ebe0f58b90be82a24a2e5 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 22 Dec 2021 07:20:53 +0000 Subject: [PATCH 58/87] upstream changelog for v3.3.3 --- CHANGES.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 857974d3f..77ab2e218 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -318,6 +318,14 @@ [#1875]: https://github.com/actix/actix-web/pull/1875 [#1878]: https://github.com/actix/actix-web/pull/1878 + +## 3.3.3 - 2021-12-18 +### Changed +* Soft-deprecate `NormalizePath::default()`, noting upcoming behavior change in v4. [#2529] + +[#2529]: https://github.com/actix/actix-web/pull/2529 + + ## 3.3.2 - 2020-12-01 ### Fixed * Removed an occasional `unwrap` on `None` panic in `NormalizePathNormalization`. [#1762] From 40a016207461d2ec2167f5614709da85994216e3 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 22 Dec 2021 07:58:37 +0000 Subject: [PATCH 59/87] add tests to scope and resource for returning from fns --- src/app.rs | 36 ++++++++++++++++++------------------ src/resource.rs | 32 +++++++++++++++++++++++++++++++- src/scope.rs | 31 ++++++++++++++++++++++++++++++- 3 files changed, 79 insertions(+), 20 deletions(-) diff --git a/src/app.rs b/src/app.rs index 6bccc1ff1..b4b952734 100644 --- a/src/app.rs +++ b/src/app.rs @@ -709,24 +709,24 @@ mod tests { assert_eq!(body, Bytes::from_static(b"https://youtube.com/watch/12345")); } - /// compile-only test for returning app type from function - pub fn foreign_app_type() -> App< - impl ServiceFactory< - ServiceRequest, - Response = ServiceResponse, - Config = (), - InitError = (), - Error = Error, - >, - > { - App::new() - // logger can be removed without affecting the return type - .wrap(crate::middleware::Logger::default()) - .route("/", web::to(|| async { "hello" })) - } - #[test] - fn return_foreign_app_type() { - let _app = foreign_app_type(); + fn can_be_returned_from_fn() { + /// compile-only test for returning app type from function + pub fn my_app() -> App< + impl ServiceFactory< + ServiceRequest, + Response = ServiceResponse, + Config = (), + InitError = (), + Error = Error, + >, + > { + App::new() + // logger can be removed without affecting the return type + .wrap(crate::middleware::Logger::default()) + .route("/", web::to(|| async { "hello" })) + } + + let _ = init_service(my_app()); } } diff --git a/src/resource.rs b/src/resource.rs index 53104930a..c13544063 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -505,18 +505,48 @@ mod tests { use actix_service::Service; use actix_utils::future::ok; + use super::*; use crate::{ guard, http::{ header::{self, HeaderValue}, Method, StatusCode, }, - middleware::DefaultHeaders, + middleware::{Compat, DefaultHeaders}, service::{ServiceRequest, ServiceResponse}, test::{call_service, init_service, TestRequest}, web, App, Error, HttpMessage, HttpResponse, }; + #[test] + fn can_be_returned_from_fn() { + fn my_resource() -> Resource { + web::resource("/test").route(web::get().to(|| async { "hello" })) + } + + fn my_compat_resource() -> Resource< + impl ServiceFactory< + ServiceRequest, + Config = (), + Response = ServiceResponse, + Error = Error, + InitError = (), + >, + > { + web::resource("/test-compat") + // .wrap_fn(|req, srv| { + // let fut = srv.call(req); + // async { Ok(fut.await?.map_into_right_body::<()>()) } + // }) + .wrap(Compat::new(DefaultHeaders::new())) + .route(web::get().to(|| async { "hello" })) + } + + App::new() + .service(my_resource()) + .service(my_compat_resource()); + } + #[actix_rt::test] async fn test_middleware() { let srv = init_service( diff --git a/src/scope.rs b/src/scope.rs index c35584770..35bbb50ba 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -586,18 +586,47 @@ mod tests { use actix_utils::future::ok; use bytes::Bytes; + use super::*; use crate::{ guard, http::{ header::{self, HeaderValue}, Method, StatusCode, }, - middleware::DefaultHeaders, + middleware::{Compat, DefaultHeaders}, service::{ServiceRequest, ServiceResponse}, test::{assert_body_eq, call_service, init_service, read_body, TestRequest}, web, App, HttpMessage, HttpRequest, HttpResponse, }; + #[test] + fn can_be_returned_from_fn() { + fn my_scope() -> Scope { + web::scope("/test") + .service(web::resource("").route(web::get().to(|| async { "hello" }))) + } + + fn my_compat_scope() -> Scope< + impl ServiceFactory< + ServiceRequest, + Config = (), + Response = ServiceResponse, + Error = Error, + InitError = (), + >, + > { + web::scope("/test-compat") + // .wrap_fn(|req, srv| { + // let fut = srv.call(req); + // async { Ok(fut.await?.map_into_right_body::<()>()) } + // }) + .wrap(Compat::new(DefaultHeaders::new())) + .service(web::resource("").route(web::get().to(|| async { "hello" }))) + } + + App::new().service(my_scope()).service(my_compat_scope()); + } + #[actix_rt::test] async fn test_scope() { let srv = From 1ea619f2a1722206cddf4af0a43715fc8202a06e Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 22 Dec 2021 08:17:35 +0000 Subject: [PATCH 60/87] use dash hyphenation in changelogs --- CHANGES.md | 551 ++++++++++++++++++----------------- actix-files/CHANGES.md | 102 +++---- actix-http-test/CHANGES.md | 76 ++--- actix-http/CHANGES.md | 544 +++++++++++++++++----------------- actix-multipart/CHANGES.md | 74 ++--- actix-router/CHANGES.md | 102 +++---- actix-test/CHANGES.md | 22 +- actix-web-actors/CHANGES.md | 60 ++-- actix-web-codegen/CHANGES.md | 52 ++-- awc/CHANGES.md | 170 +++++------ 10 files changed, 877 insertions(+), 876 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 77ab2e218..0458958c5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,29 +2,29 @@ ## Unreleased - 2021-xx-xx - +- ## 4.0.0-beta.15 - 2021-12-17 ### Added * Method on `Responder` trait (`customize`) for customizing responders and `CustomizeResponder` struct. [#2510] * Implement `Debug` for `DefaultHeaders`. [#2510] ### Changed -* Align `DefaultHeader` method terminology, deprecating previous methods. [#2510] -* Response service types in `ErrorHandlers` middleware now use `ServiceResponse>` to allow changing the body type. [#2515] +- Align `DefaultHeader` method terminology, deprecating previous methods. [#2510] +- Response service types in `ErrorHandlers` middleware now use `ServiceResponse>` to allow changing the body type. [#2515] * Both variants in `ErrorHandlerResponse` now use `ServiceResponse>`. [#2515] * Rename `test::{default_service => simple_service}`. Old name is deprecated. [#2518] -* Rename `test::{read_response_json => call_and_read_body_json}`. Old name is deprecated. [#2518] -* Rename `test::{read_response => call_and_read_body}`. Old name is deprecated. [#2518] -* Relax body type and error bounds on test utilities. [#2518] - -### Removed -* Top-level `EitherExtractError` export. [#2510] -* Conversion implementations for `either` crate. [#2516] +- Rename `test::{read_response_json => call_and_read_body_json}`. Old name is deprecated. [#2518] +- Rename `test::{read_response => call_and_read_body}`. Old name is deprecated. [#2518] +- Relax body type and error bounds on test utilities. [#2518] +- +- # Removed +- Top-level `EitherExtractError` export. [#2510] +- Conversion implementations for `either` crate. [#2516] * `test::load_stream` and `test::load_body`; replace usage with `body::to_bytes`. [#2518] -[#2510]: https://github.com/actix/actix-web/pull/2510 -[#2515]: https://github.com/actix/actix-web/pull/2515 -[#2516]: https://github.com/actix/actix-web/pull/2516 +- 2510]: https://github.com/actix/actix-web/pull/2510 +- 2515]: https://github.com/actix/actix-web/pull/2515 +- 2516]: https://github.com/actix/actix-web/pull/2516 [#2518]: https://github.com/actix/actix-web/pull/2518 @@ -34,31 +34,31 @@ * `AcceptEncoding` typed header. [#2482] * `Range` typed header. [#2485] * `HttpResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] -* `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] -* Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491] -* `HttpRequest::{req_data,req_data_mut}`. [#2487] -* `ServiceResponse::into_parts`. [#2499] - -### Changed -* Rename `Accept::{mime_precedence => ranked}`. [#2480] -* Rename `Accept::{mime_preference => preference}`. [#2480] +- `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] +- Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491] +- `HttpRequest::{req_data,req_data_mut}`. [#2487] +- `ServiceResponse::into_parts`. [#2499] +- +- # Changed +- Rename `Accept::{mime_precedence => ranked}`. [#2480] +- Rename `Accept::{mime_preference => preference}`. [#2480] * Un-deprecate `App::data_factory`. [#2484] * `HttpRequest::url_for` no longer constructs URLs with query or fragment components. [#2430] -* Remove `B` (body) type parameter on `App`. [#2493] -* Add `B` (body) type parameter on `Scope`. [#2492] -* Request-local data container is no longer part of a `RequestHead`. Instead it is a distinct part of a `Request`. [#2487] - -### Fixed -* Accept wildcard `*` items in `AcceptLanguage`. [#2480] -* Re-exports `dev::{BodySize, MessageBody, SizedStream}`. They are exposed through the `body` module. [#2468] +- Remove `B` (body) type parameter on `App`. [#2493] +- Add `B` (body) type parameter on `Scope`. [#2492] +- Request-local data container is no longer part of a `RequestHead`. Instead it is a distinct part of a `Request`. [#2487] +- +- # Fixed +- Accept wildcard `*` items in `AcceptLanguage`. [#2480] +- Re-exports `dev::{BodySize, MessageBody, SizedStream}`. They are exposed through the `body` module. [#2468] * Typed headers containing lists that require one or more items now enforce this minimum. [#2482] -### Removed -* `ConnectionInfo::get`. [#2487] - +- # Removed +- `ConnectionInfo::get`. [#2487] +- [#2430]: https://github.com/actix/actix-web/pull/2430 [#2468]: https://github.com/actix/actix-web/pull/2468 -[#2480]: https://github.com/actix/actix-web/pull/2480 +- 2480]: https://github.com/actix/actix-web/pull/2480 [#2482]: https://github.com/actix/actix-web/pull/2482 [#2484]: https://github.com/actix/actix-web/pull/2484 [#2485]: https://github.com/actix/actix-web/pull/2485 @@ -75,20 +75,20 @@ [#2474]: https://github.com/actix/actix-web/pull/2474 - +- ## 4.0.0-beta.12 - 2021-11-22 ### Changed * Compress middleware's response type is now `AnyBody>`. [#2448] ### Fixed * Relax `Unpin` bound on `S` (stream) parameter of `HttpResponseBuilder::streaming`. [#2448] - +- ### Removed * `dev::ResponseBody` re-export; is function is replaced by the new `dev::AnyBody` enum. [#2446] - +- [#2446]: https://github.com/actix/actix-web/pull/2446 [#2448]: https://github.com/actix/actix-web/pull/2448 - +- ## 4.0.0-beta.11 - 2021-11-15 ### Added @@ -96,11 +96,11 @@ ### Changed * `ContentType::html` now produces `text/html; charset=utf-8` instead of `text/html`. [#2423] -* Update `actix-server` to `2.0.0-beta.9`. [#2442] +- Update `actix-server` to `2.0.0-beta.9`. [#2442] [#2423]: https://github.com/actix/actix-web/pull/2423 -[#2442]: https://github.com/actix/actix-web/pull/2442 - +- 2442]: https://github.com/actix/actix-web/pull/2442 +- ## 4.0.0-beta.10 - 2021-10-20 ### Added @@ -108,18 +108,18 @@ * `#[actix_web::test]` macro for setting up tests with a runtime. [#2409] ### Changed -* Associated type `FromRequest::Config` was removed. [#2233] -* Inner field made private on `web::Payload`. [#2384] +- Associated type `FromRequest::Config` was removed. [#2233] +- Inner field made private on `web::Payload`. [#2384] * `Data::into_inner` and `Data::get_ref` no longer requires `T: Sized`. [#2403] * Updated rustls to v0.20. [#2414] -* Minimum supported Rust version (MSRV) is now 1.52. - -### Removed -* Useless `ServiceResponse::checked_expr` method. [#2401] - +- Minimum supported Rust version (MSRV) is now 1.52. +- +- # Removed +- Useless `ServiceResponse::checked_expr` method. [#2401] +- [#2233]: https://github.com/actix/actix-web/pull/2233 [#2362]: https://github.com/actix/actix-web/pull/2362 -[#2384]: https://github.com/actix/actix-web/pull/2384 +- 2384]: https://github.com/actix/actix-web/pull/2384 [#2401]: https://github.com/actix/actix-web/pull/2401 [#2403]: https://github.com/actix/actix-web/pull/2403 [#2409]: https://github.com/actix/actix-web/pull/2409 @@ -132,17 +132,17 @@ ### Changed * Compress middleware will return 406 Not Acceptable when no content encoding is acceptable to the client. [#2344] -* Move `BaseHttpResponse` to `dev::Response`. [#2379] +- Move `BaseHttpResponse` to `dev::Response`. [#2379] * Enable `TestRequest::param` to accept more than just static strings. [#2172] * Minimum supported Rust version (MSRV) is now 1.51. - -### Fixed -* Fix quality parse error in Accept-Encoding header. [#2344] -* Re-export correct type at `web::HttpResponse`. [#2379] +- +- # Fixed +- Fix quality parse error in Accept-Encoding header. [#2344] +- Re-export correct type at `web::HttpResponse`. [#2379] [#2172]: https://github.com/actix/actix-web/pull/2172 -[#2325]: https://github.com/actix/actix-web/pull/2325 -[#2344]: https://github.com/actix/actix-web/pull/2344 +- 2325]: https://github.com/actix/actix-web/pull/2325 +- 2344]: https://github.com/actix/actix-web/pull/2344 [#2379]: https://github.com/actix/actix-web/pull/2379 @@ -152,18 +152,18 @@ * Add extractors for `Uri` and `Method`. [#2263] * Add extractors for `ConnectionInfo` and `PeerAddr`. [#2263] * Add `Route::service` for using hand-written services as handlers. [#2262] - -### Changed -* Change compression algorithm features flags. [#2250] -* Deprecate `App::data` and `App::data_factory`. [#2271] +- +- # Changed +- Change compression algorithm features flags. [#2250] +- Deprecate `App::data` and `App::data_factory`. [#2271] * Smarter extraction of `ConnectionInfo` parts. [#2282] -### Fixed -* Scope and Resource middleware can access data items set on their own layer. [#2288] - +- # Fixed +- Scope and Resource middleware can access data items set on their own layer. [#2288] +- [#2177]: https://github.com/actix/actix-web/pull/2177 [#2250]: https://github.com/actix/actix-web/pull/2250 -[#2271]: https://github.com/actix/actix-web/pull/2271 +- 2271]: https://github.com/actix/actix-web/pull/2271 [#2262]: https://github.com/actix/actix-web/pull/2262 [#2263]: https://github.com/actix/actix-web/pull/2263 [#2282]: https://github.com/actix/actix-web/pull/2282 @@ -176,23 +176,23 @@ ### Changed * Adjusted default JSON payload limit to 2MB (from 32kb) and included size and limits in the `JsonPayloadError::Overflow` error variant. [#2162] -[#2162]: (https://github.com/actix/actix-web/pull/2162) +- 2162]: (https://github.com/actix/actix-web/pull/2162) * `ServiceResponse::error_response` now uses body type of `Body`. [#2201] * `ServiceResponse::checked_expr` now returns a `Result`. [#2201] -* Update `language-tags` to `0.3`. +- Update `language-tags` to `0.3`. * `ServiceResponse::take_body`. [#2201] -* `ServiceResponse::map_body` closure receives and returns `B` instead of `ResponseBody` types. [#2201] -* All error trait bounds in server service builders have changed from `Into` to `Into>`. [#2253] -* All error trait bounds in message body and stream impls changed from `Into` to `Into>`. [#2253] -* `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226] -* `middleware::normalize` now will not try to normalize URIs with no valid path [#2246] - -### Removed -* `HttpResponse::take_body` and old `HttpResponse::into_body` method that casted body type. [#2201] - +- `ServiceResponse::map_body` closure receives and returns `B` instead of `ResponseBody` types. [#2201] +- All error trait bounds in server service builders have changed from `Into` to `Into>`. [#2253] +- All error trait bounds in message body and stream impls changed from `Into` to `Into>`. [#2253] +- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226] +- `middleware::normalize` now will not try to normalize URIs with no valid path [#2246] +- +- # Removed +- `HttpResponse::take_body` and old `HttpResponse::into_body` method that casted body type. [#2201] +- [#2200]: https://github.com/actix/actix-web/pull/2200 [#2201]: https://github.com/actix/actix-web/pull/2201 -[#2253]: https://github.com/actix/actix-web/pull/2253 +- 2253]: https://github.com/actix/actix-web/pull/2253 [#2246]: https://github.com/actix/actix-web/pull/2246 @@ -202,11 +202,11 @@ ### Changed * Most error types are now marked `#[non_exhaustive]`. [#2148] -* Methods on `ContentDisposition` that took `T: AsRef` now take `impl AsRef`. +- Methods on `ContentDisposition` that took `T: AsRef` now take `impl AsRef`. [#2065]: https://github.com/actix/actix-web/pull/2065 -[#2148]: https://github.com/actix/actix-web/pull/2148 - +- 2148]: https://github.com/actix/actix-web/pull/2148 +- ## 4.0.0-beta.5 - 2021-04-02 ### Added @@ -214,20 +214,20 @@ * Added `TestServer::client_headers` method. [#2097] ### Fixed -* Double ampersand in Logger format is escaped correctly. [#2067] - +- Double ampersand in Logger format is escaped correctly. [#2067] +- ### Changed * `CustomResponder` would return error as `HttpResponse` when `CustomResponder::with_header` failed - instead of skipping. (Only the first error is kept when multiple error occur) [#2093] +- instead of skipping. (Only the first error is kept when multiple error occur) [#2093] ### Removed -* The `client` mod was removed. Clients should now use `awc` directly. +- The `client` mod was removed. Clients should now use `awc` directly. [871ca5e4](https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7) * Integration testing was moved to new `actix-test` crate. Namely these items from the `test` module: `TestServer`, `TestServerConfig`, `start`, `start_with`, and `unused_addr`. [#2112] - +- [#2067]: https://github.com/actix/actix-web/pull/2067 -[#2093]: https://github.com/actix/actix-web/pull/2093 +- 2093]: https://github.com/actix/actix-web/pull/2093 [#2094]: https://github.com/actix/actix-web/pull/2094 [#2097]: https://github.com/actix/actix-web/pull/2097 [#2112]: https://github.com/actix/actix-web/pull/2112 @@ -239,8 +239,8 @@ * `JsonBody::new` returns a default limit of 32kB to be consistent with `JsonConfig` and the default behaviour of the `web::Json` extractor. [#2010] -[#1981]: https://github.com/actix/actix-web/pull/1981 -[#2010]: https://github.com/actix/actix-web/pull/2010 +- 1981]: https://github.com/actix/actix-web/pull/1981 +- 2010]: https://github.com/actix/actix-web/pull/2010 ## 4.0.0-beta.3 - 2021-02-10 @@ -248,36 +248,36 @@ ## 4.0.0-beta.2 - 2021-02-10 -### Added +- # Added * The method `Either, web::Form>::into_inner()` which returns the inner type for whichever variant was created. Also works for `Either, web::Json>`. [#1894] * Add `services!` macro for helping register multiple services to `App`. [#1933] * Enable registering a vec of services of the same type to `App` [#1933] - +- ### Changed -* Rework `Responder` trait to be sync and returns `Response`/`HttpResponse` directly. - Making it simpler and more performant. [#1891] +- Rework `Responder` trait to be sync and returns `Response`/`HttpResponse` directly. +- Making it simpler and more performant. [#1891] * `ServiceRequest::into_parts` and `ServiceRequest::from_parts` can no longer fail. [#1893] * `ServiceRequest::from_request` can no longer fail. [#1893] -* Our `Either` type now uses `Left`/`Right` variants (instead of `A`/`B`) [#1894] +- Our `Either` type now uses `Left`/`Right` variants (instead of `A`/`B`) [#1894] * `test::{call_service, read_response, read_response_json, send_request}` take `&Service` - in argument [#1905] -* `App::wrap_fn`, `Resource::wrap_fn` and `Scope::wrap_fn` provide `&Service` in closure - argument. [#1905] -* `web::block` no longer requires the output is a Result. [#1957] +- in argument [#1905] +- `App::wrap_fn`, `Resource::wrap_fn` and `Scope::wrap_fn` provide `&Service` in closure +- argument. [#1905] +- `web::block` no longer requires the output is a Result. [#1957] -### Fixed +- # Fixed * Multiple calls to `App::data` with the same type now keeps the latest call's data. [#1906] - +- ### Removed * Public field of `web::Path` has been made private. [#1894] -* Public field of `web::Query` has been made private. [#1894] +- Public field of `web::Query` has been made private. [#1894] * `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869] * `AppService::set_service_data`; for custom HTTP service factories adding application data, use the - layered data model by calling `ServiceRequest::add_data_container` when handling - requests instead. [#1906] - -[#1891]: https://github.com/actix/actix-web/pull/1891 +- layered data model by calling `ServiceRequest::add_data_container` when handling +- requests instead. [#1906] +- +- 1891]: https://github.com/actix/actix-web/pull/1891 [#1893]: https://github.com/actix/actix-web/pull/1893 [#1894]: https://github.com/actix/actix-web/pull/1894 [#1869]: https://github.com/actix/actix-web/pull/1869 @@ -293,26 +293,26 @@ `Compress` to be used in `middleware::Condition` and `Resource`, `Scope` services. [#1865] ### Changed -* Update `actix-*` dependencies to tokio `1.0` based versions. [#1813] +- Update `actix-*` dependencies to tokio `1.0` based versions. [#1813] * Bumped `rand` to `0.8`. * Update `rust-tls` to `0.19`. [#1813] * Rename `Handler` to `HandlerService` and rename `Factory` to `Handler`. [#1852] -* The default `TrailingSlash` is now `Trim`, in line with existing documentation. See migration - guide for implications. [#1875] -* Rename `DefaultHeaders::{content_type => add_content_type}`. [#1875] -* MSRV is now 1.46.0. - +- The default `TrailingSlash` is now `Trim`, in line with existing documentation. See migration +- guide for implications. [#1875] +- Rename `DefaultHeaders::{content_type => add_content_type}`. [#1875] +- MSRV is now 1.46.0. +- ### Fixed -* Added the underlying parse error to `test::read_body_json`'s panic message. [#1812] - +- Added the underlying parse error to `test::read_body_json`'s panic message. [#1812] +- ### Removed * Public modules `middleware::{normalize, err_handlers}`. All necessary middleware structs are now - exposed directly by the `middleware` module. +- exposed directly by the `middleware` module. * Remove `actix-threadpool` as dependency. `actix_threadpool::BlockingError` error type can be imported from `actix_web::error` module. [#1878] - +- [#1812]: https://github.com/actix/actix-web/pull/1812 -[#1813]: https://github.com/actix/actix-web/pull/1813 +- 1813]: https://github.com/actix/actix-web/pull/1813 [#1852]: https://github.com/actix/actix-web/pull/1852 [#1865]: https://github.com/actix/actix-web/pull/1865 [#1875]: https://github.com/actix/actix-web/pull/1875 @@ -325,16 +325,16 @@ [#2529]: https://github.com/actix/actix-web/pull/2529 - +- ## 3.3.2 - 2020-12-01 ### Fixed * Removed an occasional `unwrap` on `None` panic in `NormalizePathNormalization`. [#1762] * Fix `match_pattern()` returning `None` for scope with empty path resource. [#1798] * Increase minimum `socket2` version. [#1803] -[#1762]: https://github.com/actix/actix-web/pull/1762 -[#1798]: https://github.com/actix/actix-web/pull/1798 -[#1803]: https://github.com/actix/actix-web/pull/1803 +- 1762]: https://github.com/actix/actix-web/pull/1762 +- 1798]: https://github.com/actix/actix-web/pull/1798 +- 1803]: https://github.com/actix/actix-web/pull/1803 ## 3.3.1 - 2020-11-29 @@ -342,15 +342,15 @@ ## 3.3.0 - 2020-11-25 -### Added +- # Added * Add `Either` extractor helper. [#1788] ### Changed * Upgrade `serde_urlencoded` to `0.7`. [#1773] - +- [#1773]: https://github.com/actix/actix-web/pull/1773 [#1788]: https://github.com/actix/actix-web/pull/1788 - +- ## 3.2.0 - 2020-10-30 ### Added @@ -358,17 +358,17 @@ * Add request-local data extractor `web::ReqData`. [#1748] * Add ability to register closure for request middleware logging. [#1749] * Add `app_data` to `ServiceConfig`. [#1757] -* Expose `on_connect` for access to the connection stream before request is handled. [#1754] - -### Changed -* Updated actix-web-codegen dependency for access to new `#[route(...)]` multi-method macro. -* Print non-configured `Data` type when attempting extraction. [#1743] +- Expose `on_connect` for access to the connection stream before request is handled. [#1754] +- +- # Changed +- Updated actix-web-codegen dependency for access to new `#[route(...)]` multi-method macro. +- Print non-configured `Data` type when attempting extraction. [#1743] * Re-export bytes::Buf{Mut} in web module. [#1750] * Upgrade `pin-project` to `1.0`. - -[#1723]: https://github.com/actix/actix-web/pull/1723 -[#1743]: https://github.com/actix/actix-web/pull/1743 -[#1748]: https://github.com/actix/actix-web/pull/1748 +- +- 1723]: https://github.com/actix/actix-web/pull/1723 +- 1743]: https://github.com/actix/actix-web/pull/1743 +- 1748]: https://github.com/actix/actix-web/pull/1748 [#1750]: https://github.com/actix/actix-web/pull/1750 [#1754]: https://github.com/actix/actix-web/pull/1754 [#1749]: https://github.com/actix/actix-web/pull/1749 @@ -380,13 +380,13 @@ to retain any trailing slashes. [#1695] * Remove bound `std::marker::Sized` from `web::Data` to support storing `Arc` via `web::Data::from` [#1710] - +- ### Fixed -* `ResourceMap` debug printing is no longer infinitely recursive. [#1708] +- `ResourceMap` debug printing is no longer infinitely recursive. [#1708] [#1695]: https://github.com/actix/actix-web/pull/1695 [#1708]: https://github.com/actix/actix-web/pull/1708 -[#1710]: https://github.com/actix/actix-web/pull/1710 +- 1710]: https://github.com/actix/actix-web/pull/1710 ## 3.0.2 - 2020-09-15 @@ -395,33 +395,33 @@ [#1678]: https://github.com/actix/actix-web/pull/1678 - +- ## 3.0.1 - 2020-09-13 ### Changed * `middleware::normalize::TrailingSlash` enum is now accessible. [#1673] [#1673]: https://github.com/actix/actix-web/pull/1673 - +- ## 3.0.0 - 2020-09-11 * No significant changes from `3.0.0-beta.4`. ## 3.0.0-beta.4 - 2020-09-09 -### Added +- # Added * `middleware::NormalizePath` now has configurable behavior for either always having a trailing slash, or as the new addition, always trimming trailing slashes. [#1639] ### Changed -* Update actix-codec and actix-utils dependencies. [#1634] +- Update actix-codec and actix-utils dependencies. [#1634] * `FormConfig` and `JsonConfig` configurations are now also considered when set using `App::data`. [#1641] * `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`. [#1655] -* `HttpServer::maxconnrate` is renamed to the more expressive - `HttpServer::max_connection_rate`. [#1655] +- `HttpServer::maxconnrate` is renamed to the more expressive +- `HttpServer::max_connection_rate`. [#1655] -[#1639]: https://github.com/actix/actix-web/pull/1639 -[#1641]: https://github.com/actix/actix-web/pull/1641 +- 1639]: https://github.com/actix/actix-web/pull/1639 +- 1641]: https://github.com/actix/actix-web/pull/1641 [#1634]: https://github.com/actix/actix-web/pull/1634 [#1655]: https://github.com/actix/actix-web/pull/1655 @@ -431,22 +431,22 @@ ## 3.0.0-beta.2 - 2020-08-17 -### Changed +- # Changed * `PayloadConfig` is now also considered in `Bytes` and `String` extractors when set using `App::data`. [#1610] * `web::Path` now has a public representation: `web::Path(pub T)` that enables destructuring. [#1594] -* `ServiceRequest::app_data` allows retrieval of non-Data data without splitting into parts to +- `ServiceRequest::app_data` allows retrieval of non-Data data without splitting into parts to access `HttpRequest` which already allows this. [#1618] -* Re-export all error types from `awc`. [#1621] +- Re-export all error types from `awc`. [#1621] * MSRV is now 1.42.0. - +- ### Fixed -* Memory leak of app data in pooled requests. [#1609] - +- Memory leak of app data in pooled requests. [#1609] +- [#1594]: https://github.com/actix/actix-web/pull/1594 [#1609]: https://github.com/actix/actix-web/pull/1609 -[#1610]: https://github.com/actix/actix-web/pull/1610 +- 1610]: https://github.com/actix/actix-web/pull/1610 [#1618]: https://github.com/actix/actix-web/pull/1618 [#1621]: https://github.com/actix/actix-web/pull/1621 @@ -457,29 +457,29 @@ * `HttpRequest::match_pattern` and `ServiceRequest::match_pattern` for extracting the matched resource pattern. * `HttpRequest::match_name` and `ServiceRequest::match_name` for extracting matched resource name. - -### Changed +- +- # Changed * Fix actix_http::h1::dispatcher so it returns when HW_BUFFER_SIZE is reached. Should reduce peak memory consumption during large uploads. [#1550] -* Migrate cookie handling to `cookie` crate. Actix-web no longer requires `ring` dependency. +- Migrate cookie handling to `cookie` crate. Actix-web no longer requires `ring` dependency. * MSRV is now 1.41.1 -### Fixed -* `NormalizePath` improved consistency when path needs slashes added _and_ removed. - +- # Fixed +- `NormalizePath` improved consistency when path needs slashes added _and_ removed. +- ## 3.0.0-alpha.3 - 2020-05-21 -### Added +- # Added * Add option to create `Data` from `Arc` [#1509] ### Changed * Resources and Scopes can now access non-overridden data types set on App (or containing scopes) when setting their own data. [#1486] -* Fix audit issue logging by default peer address [#1485] +- Fix audit issue logging by default peer address [#1485] * Bump minimum supported Rust version to 1.40 * Replace deprecated `net2` crate with `socket2` - -[#1485]: https://github.com/actix/actix-web/pull/1485 -[#1509]: https://github.com/actix/actix-web/pull/1509 - +- +- 1485]: https://github.com/actix/actix-web/pull/1485 +- 1509]: https://github.com/actix/actix-web/pull/1509 +- ## [3.0.0-alpha.2] - 2020-05-08 ### Changed @@ -488,10 +488,10 @@ * Implement `std::error::Error` for our custom errors [#1422] * NormalizePath middleware now appends trailing / so that routes of form /example/ respond to /example requests. [#1433] * Remove the `failure` feature and support. - -[#1422]: https://github.com/actix/actix-web/pull/1422 -[#1433]: https://github.com/actix/actix-web/pull/1433 -[#1452]: https://github.com/actix/actix-web/pull/1452 +- +- 1422]: https://github.com/actix/actix-web/pull/1422 +- 1433]: https://github.com/actix/actix-web/pull/1433 +- 1452]: https://github.com/actix/actix-web/pull/1452 [#1486]: https://github.com/actix/actix-web/pull/1486 @@ -503,16 +503,16 @@ * Add convenience functions `test::read_body_json()` and `test::TestRequest::send_request()` for testing. ### Changed - -* Use `sha-1` crate instead of unmaintained `sha1` crate +- +- Use `sha-1` crate instead of unmaintained `sha1` crate * Skip empty chunks when returning response from a `Stream` [#1308] * Update the `time` dependency to 0.2.7 * Update `actix-tls` dependency to 2.0.0-alpha.1 -* Update `rustls` dependency to 0.17 - -[#1308]: https://github.com/actix/actix-web/pull/1308 - -## [2.0.0] - 2019-12-25 +- Update `rustls` dependency to 0.17 +- +- 1308]: https://github.com/actix/actix-web/pull/1308 +- +- [2.0.0] - 2019-12-25 ### Changed @@ -520,404 +520,405 @@ * Allow to gracefully stop test server via `TestServer::stop()` -* Allow to specify multi-patterns for resources +- Allow to specify multi-patterns for resources -## [2.0.0-rc] - 2019-12-20 +- [2.0.0-rc] - 2019-12-20 -### Changed +- # Changed * Move `BodyEncoding` to `dev` module #1220 * Allow to set `peer_addr` for TestRequest #1074 -* Make web::Data deref to Arc #1214 +- Make web::Data deref to Arc #1214 -* Rename `App::register_data()` to `App::app_data()` +- Rename `App::register_data()` to `App::app_data()` -* `HttpRequest::app_data()` returns `Option<&T>` instead of `Option<&Data>` +- `HttpRequest::app_data()` returns `Option<&T>` instead of `Option<&Data>` -### Fixed +- # Fixed -* Fix `AppConfig::secure()` is always false. #1202 +- Fix `AppConfig::secure()` is always false. #1202 ## [2.0.0-alpha.6] - 2019-12-15 - +- ### Fixed * Fixed compilation with default features off ## [2.0.0-alpha.5] - 2019-12-13 -### Added +- # Added * Add test server, `test::start()` and `test::start_with()` ## [2.0.0-alpha.4] - 2019-12-08 -### Deleted +- # Deleted * Delete HttpServer::run(), it is not useful with async/await ## [2.0.0-alpha.3] - 2019-12-07 -### Changed +- # Changed * Migrate to tokio 0.2 ## [2.0.0-alpha.1] - 2019-11-22 - +- ### Changed * Migrated to `std::future` * Remove implementation of `Responder` for `()`. (#1167) - +- ## [1.0.9] - 2019-11-14 - +- ### Added * Add `Payload::into_inner` method and make stored `def::Payload` public. (#1110) ### Changed -* Support `Host` guards when the `Host` header is unset (e.g. HTTP/2 requests) (#1129) +- Support `Host` guards when the `Host` header is unset (e.g. HTTP/2 requests) (#1129) ## [1.0.8] - 2019-09-25 - +- ### Added * Add `Scope::register_data` and `Resource::register_data` methods, parallel to `App::register_data`. * Add `middleware::Condition` that conditionally enables another middleware - +- * Allow to re-construct `ServiceRequest` from `HttpRequest` and `Payload` -* Add `HttpServer::listen_uds` for ability to listen on UDS FD rather than path, +- Add `HttpServer::listen_uds` for ability to listen on UDS FD rather than path, which is useful for example with systemd. - +- ### Changed - +- * Make UrlEncodedError::Overflow more informative * Use actix-testing for testing utils - +- ## [1.0.7] - 2019-08-29 - +- ### Fixed * Request Extensions leak #1062 ## [1.0.6] - 2019-08-28 - +- ### Added * Re-implement Host predicate (#989) * Form implements Responder, returning a `application/x-www-form-urlencoded` response -* Add `into_inner` to `Data` +- Add `into_inner` to `Data` -* Add `test::TestRequest::set_form()` convenience method to automatically serialize data and set +- Add `test::TestRequest::set_form()` convenience method to automatically serialize data and set the header in test requests. - +- ### Changed - +- * `Query` payload made `pub`. Allows user to pattern-match the payload. * Enable `rust-tls` feature for client #1045 -* Update serde_urlencoded to 0.6.1 - -* Update url to 2.1 +- Update serde_urlencoded to 0.6.1 +- Update url to 2.1 +- ## [1.0.5] - 2019-07-18 - +- ### Added * Unix domain sockets (HttpServer::bind_uds) #92 * Actix now logs errors resulting in "internal server error" responses always, with the `error` logging level - +- ### Fixed - +- * Restored logging of errors through the `Logger` middleware ## [1.0.4] - 2019-07-17 - +- ### Added * Add `Responder` impl for `(T, StatusCode) where T: Responder` * Allow to access app's resource map via `ServiceRequest::resource_map()` and `HttpRequest::resource_map()` methods. - +- ### Changed - +- * Upgrade `rand` dependency version to 0.7 ## [1.0.3] - 2019-06-28 - +- ### Added * Support asynchronous data factories #850 ### Changed -* Use `encoding_rs` crate instead of unmaintained `encoding` crate +- Use `encoding_rs` crate instead of unmaintained `encoding` crate ## [1.0.2] - 2019-06-17 - +- ### Changed * Move cors middleware to `actix-cors` crate. * Move identity middleware to `actix-identity` crate. - +- ## [1.0.1] - 2019-06-17 - +- ### Added * Add support for PathConfig #903 * Add `middleware::identity::RequestIdentity` trait to `get_identity` from `HttpMessage`. -### Changed +- # Changed -* Move cors middleware to `actix-cors` crate. +- Move cors middleware to `actix-cors` crate. * Move identity middleware to `actix-identity` crate. -* Disable default feature `secure-cookies`. +- Disable default feature `secure-cookies`. -* Allow to test an app that uses async actors #897 +- Allow to test an app that uses async actors #897 -* Re-apply patch from #637 #894 +- Re-apply patch from #637 #894 -### Fixed +- # Fixed -* HttpRequest::url_for is broken with nested scopes #915 +- HttpRequest::url_for is broken with nested scopes #915 ## [1.0.0] - 2019-06-05 - +- ### Added * Add `Scope::configure()` method. * Add `ServiceRequest::set_payload()` method. -* Add `test::TestRequest::set_json()` convenience method to automatically +- Add `test::TestRequest::set_json()` convenience method to automatically serialize data and set header in test requests. - +- * Add macros for head, options, trace, connect and patch http methods - +- ### Changed -* Drop an unnecessary `Option<_>` indirection around `ServerBuilder` from `HttpServer`. #863 +- Drop an unnecessary `Option<_>` indirection around `ServerBuilder` from `HttpServer`. #863 ### Fixed -* Fix Logger request time format, and use rfc3339. #867 +- Fix Logger request time format, and use rfc3339. #867 * Clear http requests pool on app service drop #860 - +- ## [1.0.0-rc] - 2019-05-18 - +- ### Added * Add `Query::from_query()` to extract parameters from a query string. #846 * `QueryConfig`, similar to `JsonConfig` for customizing error handling of query extractors. ### Changed - -* `JsonConfig` is now `Send + Sync`, this implies that `error_handler` must be `Send + Sync` too. +- +- `JsonConfig` is now `Send + Sync`, this implies that `error_handler` must be `Send + Sync` too. ### Fixed -* Codegen with parameters in the path only resolves the first registered endpoint #841 +- Codegen with parameters in the path only resolves the first registered endpoint #841 ## [1.0.0-beta.4] - 2019-05-12 - +- ### Added * Allow to set/override app data on scope level ### Changed -* `App::configure` take an `FnOnce` instead of `Fn` +- `App::configure` take an `FnOnce` instead of `Fn` * Upgrade actix-net crates -## [1.0.0-beta.3] - 2019-05-04 - +- [1.0.0-beta.3] - 2019-05-04 +- ### Added * Add helper function for executing futures `test::block_fn()` ### Changed -* Extractor configuration could be registered with `App::data()` +- Extractor configuration could be registered with `App::data()` or with `Resource::data()` #775 * Route data is unified with app data, `Route::data()` moved to resource - level to `Resource::data()` +- level to `Resource::data()` * CORS handling without headers #702 - +- * Allow constructing `Data` instances to avoid double `Arc` for `Send + Sync` types. -### Fixed +- # Fixed -* Fix `NormalizePath` middleware impl #806 +- Fix `NormalizePath` middleware impl #806 ### Deleted -* `App::data_factory()` is deleted. +- `App::data_factory()` is deleted. ## [1.0.0-beta.2] - 2019-04-24 - +- ### Added * Add raw services support via `web::service()` * Add helper functions for reading response body `test::read_body()` -* Add support for `remainder match` (i.e "/path/{tail}*") +- Add support for `remainder match` (i.e "/path/{tail}*") -* Extend `Responder` trait, allow to override status code and headers. +- Extend `Responder` trait, allow to override status code and headers. -* Store visit and login timestamp in the identity cookie #502 +- Store visit and login timestamp in the identity cookie #502 -### Changed +- # Changed -* `.to_async()` handler can return `Responder` type #792 +- `.to_async()` handler can return `Responder` type #792 ### Fixed -* Fix async web::Data factory handling +- Fix async web::Data factory handling ## [1.0.0-beta.1] - 2019-04-20 - +- ### Added * Add helper functions for reading test response body, `test::read_response()` and test::read_response_json()` * Add `.peer_addr()` #744 - +- * Add `NormalizePath` middleware -### Changed +- # Changed -* Rename `RouterConfig` to `ServiceConfig` +- Rename `RouterConfig` to `ServiceConfig` * Rename `test::call_success` to `test::call_service` -* Removed `ServiceRequest::from_parts()` as it is unsafe to create from parts. +- Removed `ServiceRequest::from_parts()` as it is unsafe to create from parts. -* `CookieIdentityPolicy::max_age()` accepts value in seconds +- `CookieIdentityPolicy::max_age()` accepts value in seconds -### Fixed +- # Fixed -* Fixed `TestRequest::app_data()` +- Fixed `TestRequest::app_data()` ## [1.0.0-alpha.6] - 2019-04-14 - +- ### Changed * Allow using any service as default service. * Remove generic type for request payload, always use default. -* Removed `Decompress` middleware. Bytes, String, Json, Form extractors +- Removed `Decompress` middleware. Bytes, String, Json, Form extractors automatically decompress payload. - +- * Make extractor config type explicit. Add `FromRequest::Config` associated type. - +- ## [1.0.0-alpha.5] - 2019-04-12 - +- ### Added * Added async io `TestBuffer` for testing. ### Deleted -* Removed native-tls support +- Removed native-tls support ## [1.0.0-alpha.4] - 2019-04-08 - +- ### Added * `App::configure()` allow to offload app configuration to different methods * Added `URLPath` option for logger -* Added `ServiceRequest::app_data()`, returns `Data` +- Added `ServiceRequest::app_data()`, returns `Data` -* Added `ServiceFromRequest::app_data()`, returns `Data` +- Added `ServiceFromRequest::app_data()`, returns `Data` -### Changed +- # Changed -* `FromRequest` trait refactoring +- `FromRequest` trait refactoring * Move multipart support to actix-multipart crate -### Fixed +- # Fixed -* Fix body propagation in Response::from_error. #760 +- Fix body propagation in Response::from_error. #760 ## [1.0.0-alpha.3] - 2019-04-02 - +- ### Changed * Renamed `TestRequest::to_service()` to `TestRequest::to_srv_request()` * Renamed `TestRequest::to_response()` to `TestRequest::to_srv_response()` -* Removed `Deref` impls +- Removed `Deref` impls -### Removed +- # Removed -* Removed unused `actix_web::web::md()` +- Removed unused `actix_web::web::md()` ## [1.0.0-alpha.2] - 2019-03-29 - +- ### Added * Rustls support ### Changed -* Use forked cookie +- Use forked cookie * Multipart::Field renamed to MultipartField -## [1.0.0-alpha.1] - 2019-03-28 +- [1.0.0-alpha.1] - 2019-03-28 -### Changed +- # Changed * Complete architecture re-design. * Return 405 response if no matching route found within resource #538 +- - diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index d6b39e28f..ef8eba0fc 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -4,42 +4,42 @@ ## 0.6.0-beta.10 - 2021-12-11 -* No significant changes since `0.6.0-beta.9`. +- No significant changes since `0.6.0-beta.9`. ## 0.6.0-beta.9 - 2021-11-22 -* Add crate feature `experimental-io-uring`, enabling async file I/O to be utilized. This feature is only available on Linux OSes with recent kernel versions. This feature is semver-exempt. [#2408] -* Add `NamedFile::open_async`. [#2408] -* Fix 304 Not Modified responses to omit the Content-Length header, as per the spec. [#2453] -* The `Responder` impl for `NamedFile` now has a boxed future associated type. [#2408] -* The `Service` impl for `NamedFileService` now has a boxed future associated type. [#2408] -* Add `impl Clone` for `FilesService`. [#2408] +- Add crate feature `experimental-io-uring`, enabling async file I/O to be utilized. This feature is only available on Linux OSes with recent kernel versions. This feature is semver-exempt. [#2408] +- Add `NamedFile::open_async`. [#2408] +- Fix 304 Not Modified responses to omit the Content-Length header, as per the spec. [#2453] +- The `Responder` impl for `NamedFile` now has a boxed future associated type. [#2408] +- The `Service` impl for `NamedFileService` now has a boxed future associated type. [#2408] +- Add `impl Clone` for `FilesService`. [#2408] [#2408]: https://github.com/actix/actix-web/pull/2408 [#2453]: https://github.com/actix/actix-web/pull/2453 ## 0.6.0-beta.8 - 2021-10-20 -* Minimum supported Rust version (MSRV) is now 1.52. +- Minimum supported Rust version (MSRV) is now 1.52. ## 0.6.0-beta.7 - 2021-09-09 -* Minimum supported Rust version (MSRV) is now 1.51. +- Minimum supported Rust version (MSRV) is now 1.51. ## 0.6.0-beta.6 - 2021-06-26 -* Added `Files::path_filter()`. [#2274] -* `Files::show_files_listing()` can now be used with `Files::index_file()` to show files listing as a fallback when the index file is not found. [#2228] +- Added `Files::path_filter()`. [#2274] +- `Files::show_files_listing()` can now be used with `Files::index_file()` to show files listing as a fallback when the index file is not found. [#2228] [#2274]: https://github.com/actix/actix-web/pull/2274 [#2228]: https://github.com/actix/actix-web/pull/2228 ## 0.6.0-beta.5 - 2021-06-17 -* `NamedFile` now implements `ServiceFactory` and `HttpServiceFactory` making it much more useful in routing. For example, it can be used directly as a default service. [#2135] -* For symbolic links, `Content-Disposition` header no longer shows the filename of the original file. [#2156] -* `Files::redirect_to_slash_directory()` now works as expected when used with `Files::show_files_listing()`. [#2225] -* `application/{javascript, json, wasm}` mime type now have `inline` disposition by default. [#2257] +- `NamedFile` now implements `ServiceFactory` and `HttpServiceFactory` making it much more useful in routing. For example, it can be used directly as a default service. [#2135] +- For symbolic links, `Content-Disposition` header no longer shows the filename of the original file. [#2156] +- `Files::redirect_to_slash_directory()` now works as expected when used with `Files::show_files_listing()`. [#2225] +- `application/{javascript, json, wasm}` mime type now have `inline` disposition by default. [#2257] [#2135]: https://github.com/actix/actix-web/pull/2135 [#2156]: https://github.com/actix/actix-web/pull/2156 @@ -48,130 +48,130 @@ ## 0.6.0-beta.4 - 2021-04-02 -* Add support for `.guard` in `Files` to selectively filter `Files` services. [#2046] +- Add support for `.guard` in `Files` to selectively filter `Files` services. [#2046] [#2046]: https://github.com/actix/actix-web/pull/2046 ## 0.6.0-beta.3 - 2021-03-09 -* No notable changes. +- No notable changes. ## 0.6.0-beta.2 - 2021-02-10 -* Fix If-Modified-Since and If-Unmodified-Since to not compare using sub-second timestamps. [#1887] -* Replace `v_htmlescape` with `askama_escape`. [#1953] +- Fix If-Modified-Since and If-Unmodified-Since to not compare using sub-second timestamps. [#1887] +- Replace `v_htmlescape` with `askama_escape`. [#1953] [#1887]: https://github.com/actix/actix-web/pull/1887 [#1953]: https://github.com/actix/actix-web/pull/1953 ## 0.6.0-beta.1 - 2021-01-07 -* `HttpRange::parse` now has its own error type. -* Update `bytes` to `1.0`. [#1813] +- `HttpRange::parse` now has its own error type. +- Update `bytes` to `1.0`. [#1813] [#1813]: https://github.com/actix/actix-web/pull/1813 ## 0.5.0 - 2020-12-26 -* Optionally support hidden files/directories. [#1811] +- Optionally support hidden files/directories. [#1811] [#1811]: https://github.com/actix/actix-web/pull/1811 ## 0.4.1 - 2020-11-24 -* Clarify order of parameters in `Files::new` and improve docs. +- Clarify order of parameters in `Files::new` and improve docs. ## 0.4.0 - 2020-10-06 -* Add `Files::prefer_utf8` option that adds UTF-8 charset on certain response types. [#1714] +- Add `Files::prefer_utf8` option that adds UTF-8 charset on certain response types. [#1714] [#1714]: https://github.com/actix/actix-web/pull/1714 ## 0.3.0 - 2020-09-11 -* No significant changes from 0.3.0-beta.1. +- No significant changes from 0.3.0-beta.1. ## 0.3.0-beta.1 - 2020-07-15 -* Update `v_htmlescape` to 0.10 -* Update `actix-web` and `actix-http` dependencies to beta.1 +- Update `v_htmlescape` to 0.10 +- Update `actix-web` and `actix-http` dependencies to beta.1 ## 0.3.0-alpha.1 - 2020-05-23 -* Update `actix-web` and `actix-http` dependencies to alpha -* Fix some typos in the docs -* Bump minimum supported Rust version to 1.40 -* Support sending Content-Length when Content-Range is specified [#1384] +- Update `actix-web` and `actix-http` dependencies to alpha +- Fix some typos in the docs +- Bump minimum supported Rust version to 1.40 +- Support sending Content-Length when Content-Range is specified [#1384] [#1384]: https://github.com/actix/actix-web/pull/1384 ## 0.2.1 - 2019-12-22 -* Use the same format for file URLs regardless of platforms +- Use the same format for file URLs regardless of platforms ## 0.2.0 - 2019-12-20 -* Fix BodyEncoding trait import #1220 +- Fix BodyEncoding trait import #1220 ## 0.2.0-alpha.1 - 2019-12-07 -* Migrate to `std::future` +- Migrate to `std::future` ## 0.1.7 - 2019-11-06 -* Add an additional `filename*` param in the `Content-Disposition` header of +- Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151) ## 0.1.6 - 2019-10-14 -* Add option to redirect to a slash-ended path `Files` #1132 +- Add option to redirect to a slash-ended path `Files` #1132 ## 0.1.5 - 2019-10-08 -* Bump up `mime_guess` crate version to 2.0.1 -* Bump up `percent-encoding` crate version to 2.1 -* Allow user defined request guards for `Files` #1113 +- Bump up `mime_guess` crate version to 2.0.1 +- Bump up `percent-encoding` crate version to 2.1 +- Allow user defined request guards for `Files` #1113 ## 0.1.4 - 2019-07-20 -* Allow to disable `Content-Disposition` header #686 +- Allow to disable `Content-Disposition` header #686 ## 0.1.3 - 2019-06-28 -* Do not set `Content-Length` header, let actix-http set it #930 +- Do not set `Content-Length` header, let actix-http set it #930 ## 0.1.2 - 2019-06-13 -* Content-Length is 0 for NamedFile HEAD request #914 -* Fix ring dependency from actix-web default features for #741 +- Content-Length is 0 for NamedFile HEAD request #914 +- Fix ring dependency from actix-web default features for #741 ## 0.1.1 - 2019-06-01 -* Static files are incorrectly served as both chunked and with length #812 +- Static files are incorrectly served as both chunked and with length #812 ## 0.1.0 - 2019-05-25 -* NamedFile last-modified check always fails due to nano-seconds in file modified date #820 +- NamedFile last-modified check always fails due to nano-seconds in file modified date #820 ## 0.1.0-beta.4 - 2019-05-12 -* Update actix-web to beta.4 +- Update actix-web to beta.4 ## 0.1.0-beta.1 - 2019-04-20 -* Update actix-web to beta.1 +- Update actix-web to beta.1 ## 0.1.0-alpha.6 - 2019-04-14 -* Update actix-web to alpha6 +- Update actix-web to alpha6 ## 0.1.0-alpha.4 - 2019-04-08 -* Update actix-web to alpha4 +- Update actix-web to alpha4 ## 0.1.0-alpha.2 - 2019-04-02 -* Add default handler support +- Add default handler support ## 0.1.0-alpha.1 - 2019-03-28 -* Initial impl +- Initial impl diff --git a/actix-http-test/CHANGES.md b/actix-http-test/CHANGES.md index 156012168..4e86e20e8 100644 --- a/actix-http-test/CHANGES.md +++ b/actix-http-test/CHANGES.md @@ -4,125 +4,125 @@ ## 3.0.0-beta.9 - 2021-12-11 -* No significant changes since `3.0.0-beta.8`. +- No significant changes since `3.0.0-beta.8`. ## 3.0.0-beta.8 - 2021-11-30 -* Update `actix-tls` to `3.0.0-rc.1`. [#2474] +- Update `actix-tls` to `3.0.0-rc.1`. [#2474] [#2474]: https://github.com/actix/actix-web/pull/2474 ## 3.0.0-beta.7 - 2021-11-22 -* Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408] +- Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408] [#2408]: https://github.com/actix/actix-web/pull/2408 ## 3.0.0-beta.6 - 2021-11-15 -* `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442] -* Update `actix-server` to `2.0.0-beta.9`. [#2442] -* Minimum supported Rust version (MSRV) is now 1.52. +- `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442] +- Update `actix-server` to `2.0.0-beta.9`. [#2442] +- Minimum supported Rust version (MSRV) is now 1.52. [#2442]: https://github.com/actix/actix-web/pull/2442 ## 3.0.0-beta.5 - 2021-09-09 -* Minimum supported Rust version (MSRV) is now 1.51. +- Minimum supported Rust version (MSRV) is now 1.51. ## 3.0.0-beta.4 - 2021-04-02 -* Added `TestServer::client_headers` method. [#2097] +- Added `TestServer::client_headers` method. [#2097] [#2097]: https://github.com/actix/actix-web/pull/2097 ## 3.0.0-beta.3 - 2021-03-09 -* No notable changes. +- No notable changes. ## 3.0.0-beta.2 - 2021-02-10 -* No notable changes. +- No notable changes. ## 3.0.0-beta.1 - 2021-01-07 -* Update `bytes` to `1.0`. [#1813] +- Update `bytes` to `1.0`. [#1813] [#1813]: https://github.com/actix/actix-web/pull/1813 ## 2.1.0 - 2020-11-25 -* Add ability to set address for `TestServer`. [#1645] -* Upgrade `base64` to `0.13`. -* Upgrade `serde_urlencoded` to `0.7`. [#1773] +- Add ability to set address for `TestServer`. [#1645] +- Upgrade `base64` to `0.13`. +- Upgrade `serde_urlencoded` to `0.7`. [#1773] [#1773]: https://github.com/actix/actix-web/pull/1773 [#1645]: https://github.com/actix/actix-web/pull/1645 ## 2.0.0 - 2020-09-11 -* Update actix-codec and actix-utils dependencies. +- Update actix-codec and actix-utils dependencies. ## 2.0.0-alpha.1 - 2020-05-23 -* Update the `time` dependency to 0.2.7 -* Update `actix-connect` dependency to 2.0.0-alpha.2 -* Make `test_server` `async` fn. -* Bump minimum supported Rust version to 1.40 -* Replace deprecated `net2` crate with `socket2` -* Update `base64` dependency to 0.12 -* Update `env_logger` dependency to 0.7 +- Update the `time` dependency to 0.2.7 +- Update `actix-connect` dependency to 2.0.0-alpha.2 +- Make `test_server` `async` fn. +- Bump minimum supported Rust version to 1.40 +- Replace deprecated `net2` crate with `socket2` +- Update `base64` dependency to 0.12 +- Update `env_logger` dependency to 0.7 ## 1.0.0 - 2019-12-13 -* Replaced `TestServer::start()` with `test_server()` +- Replaced `TestServer::start()` with `test_server()` ## 1.0.0-alpha.3 - 2019-12-07 -* Migrate to `std::future` +- Migrate to `std::future` ## 0.2.5 - 2019-09-17 -* Update serde_urlencoded to "0.6.1" -* Increase TestServerRuntime timeouts from 500ms to 3000ms -* Do not override current `System` +- Update serde_urlencoded to "0.6.1" +- Increase TestServerRuntime timeouts from 500ms to 3000ms +- Do not override current `System` ## 0.2.4 - 2019-07-18 -* Update actix-server to 0.6 +- Update actix-server to 0.6 ## 0.2.3 - 2019-07-16 -* Add `delete`, `options`, `patch` methods to `TestServerRunner` +- Add `delete`, `options`, `patch` methods to `TestServerRunner` ## 0.2.2 - 2019-06-16 -* Add .put() and .sput() methods +- Add .put() and .sput() methods ## 0.2.1 - 2019-06-05 -* Add license files +- Add license files ## 0.2.0 - 2019-05-12 -* Update awc and actix-http deps +- Update awc and actix-http deps ## 0.1.1 - 2019-04-24 -* Always make new connection for http client +- Always make new connection for http client ## 0.1.0 - 2019-04-16 -* No changes +- No changes ## 0.1.0-alpha.3 - 2019-04-02 -* Request functions accept path #743 +- Request functions accept path #743 ## 0.1.0-alpha.2 - 2019-03-29 -* Added TestServerRuntime::load_body() method -* Update actix-http and awc libraries +- Added TestServerRuntime::load_body() method +- Update actix-http and awc libraries ## 0.1.0-alpha.1 - 2019-03-28 -* Initial impl +- Initial impl diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index ad98d132a..3b45e934f 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -2,22 +2,22 @@ ## Unreleased - 2021-xx-xx ### Changes -* `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527] +- `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527] [#2527]: https://github.com/actix/actix-web/pull/2527 ## 3.0.0-beta.16 - 2021-12-17 ### Added -* New method on `MessageBody` trait, `try_into_bytes`, with default implementation, for optimizations on body types that complete in exactly one poll. Replaces `is_complete_body` and `take_complete_body`. [#2522] +- New method on `MessageBody` trait, `try_into_bytes`, with default implementation, for optimizations on body types that complete in exactly one poll. Replaces `is_complete_body` and `take_complete_body`. [#2522] ### Changed -* Rename trait `IntoHeaderPair => TryIntoHeaderPair`. [#2510] -* Rename `TryIntoHeaderPair::{try_into_header_pair => try_into_pair}`. [#2510] -* Rename trait `IntoHeaderValue => TryIntoHeaderValue`. [#2510] +- Rename trait `IntoHeaderPair => TryIntoHeaderPair`. [#2510] +- Rename `TryIntoHeaderPair::{try_into_header_pair => try_into_pair}`. [#2510] +- Rename trait `IntoHeaderValue => TryIntoHeaderValue`. [#2510] ### Removed -* `MessageBody::{is_complete_body,take_complete_body}`. [#2522] +- `MessageBody::{is_complete_body,take_complete_body}`. [#2522] [#2510]: https://github.com/actix/actix-web/pull/2510 [#2522]: https://github.com/actix/actix-web/pull/2522 @@ -25,43 +25,43 @@ ## 3.0.0-beta.15 - 2021-12-11 ### Added -* Add timeout for canceling HTTP/2 server side connection handshake. Default to 5 seconds. [#2483] -* HTTP/2 handshake timeout can be configured with `ServiceConfig::client_timeout`. [#2483] -* `Response::map_into_boxed_body`. [#2468] -* `body::EitherBody` enum. [#2468] -* `body::None` struct. [#2468] -* Impl `MessageBody` for `bytestring::ByteString`. [#2468] -* `impl Clone for ws::HandshakeError`. [#2468] -* `#[must_use]` for `ws::Codec` to prevent subtle bugs. [#1920] -* `impl Default ` for `ws::Codec`. [#1920] -* `header::QualityItem::{max, min}`. [#2486] -* `header::Quality::{MAX, MIN}`. [#2486] -* `impl Display` for `header::Quality`. [#2486] -* Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491] -* `Request::take_conn_data()`. [#2491] -* `Request::take_req_data()`. [#2487] -* `impl Clone` for `RequestHead`. [#2487] -* New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimizations on body types that are done in exactly one poll/chunk. [#2497] -* New `boxed` method on `MessageBody` trait for wrapping body type. [#2520] +- Add timeout for canceling HTTP/2 server side connection handshake. Default to 5 seconds. [#2483] +- HTTP/2 handshake timeout can be configured with `ServiceConfig::client_timeout`. [#2483] +- `Response::map_into_boxed_body`. [#2468] +- `body::EitherBody` enum. [#2468] +- `body::None` struct. [#2468] +- Impl `MessageBody` for `bytestring::ByteString`. [#2468] +- `impl Clone for ws::HandshakeError`. [#2468] +- `#[must_use]` for `ws::Codec` to prevent subtle bugs. [#1920] +- `impl Default ` for `ws::Codec`. [#1920] +- `header::QualityItem::{max, min}`. [#2486] +- `header::Quality::{MAX, MIN}`. [#2486] +- `impl Display` for `header::Quality`. [#2486] +- Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491] +- `Request::take_conn_data()`. [#2491] +- `Request::take_req_data()`. [#2487] +- `impl Clone` for `RequestHead`. [#2487] +- New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimizations on body types that are done in exactly one poll/chunk. [#2497] +- New `boxed` method on `MessageBody` trait for wrapping body type. [#2520] ### Changed -* Rename `body::BoxBody::{from_body => new}`. [#2468] -* Body type for `Responses` returned from `Response::{new, ok, etc...}` is now `BoxBody`. [#2468] -* The `Error` associated type on `MessageBody` type now requires `impl Error` (or similar). [#2468] -* Error types using in service builders now require `Into>`. [#2468] -* `From` implementations on error types now return a `Response`. [#2468] -* `ResponseBuilder::body(B)` now returns `Response>`. [#2468] -* `ResponseBuilder::finish()` now returns `Response>`. [#2468] +- Rename `body::BoxBody::{from_body => new}`. [#2468] +- Body type for `Responses` returned from `Response::{new, ok, etc...}` is now `BoxBody`. [#2468] +- The `Error` associated type on `MessageBody` type now requires `impl Error` (or similar). [#2468] +- Error types using in service builders now require `Into>`. [#2468] +- `From` implementations on error types now return a `Response`. [#2468] +- `ResponseBuilder::body(B)` now returns `Response>`. [#2468] +- `ResponseBuilder::finish()` now returns `Response>`. [#2468] ### Removed -* `ResponseBuilder::streaming`. [#2468] -* `impl Future` for `ResponseBuilder`. [#2468] -* Remove unnecessary `MessageBody` bound on types passed to `body::AnyBody::new`. [#2468] -* Move `body::AnyBody` to `awc`. Replaced with `EitherBody` and `BoxBody`. [#2468] -* `impl Copy` for `ws::Codec`. [#1920] -* `header::qitem` helper. Replaced with `header::QualityItem::max`. [#2486] -* `impl TryFrom` for `header::Quality`. [#2486] -* `http` module. Most everything it contained is exported at the crate root. [#2488] +- `ResponseBuilder::streaming`. [#2468] +- `impl Future` for `ResponseBuilder`. [#2468] +- Remove unnecessary `MessageBody` bound on types passed to `body::AnyBody::new`. [#2468] +- Move `body::AnyBody` to `awc`. Replaced with `EitherBody` and `BoxBody`. [#2468] +- `impl Copy` for `ws::Codec`. [#1920] +- `header::qitem` helper. Replaced with `header::QualityItem::max`. [#2486] +- `impl TryFrom` for `header::Quality`. [#2486] +- `http` module. Most everything it contained is exported at the crate root. [#2488] [#2483]: https://github.com/actix/actix-web/pull/2483 [#2468]: https://github.com/actix/actix-web/pull/2468 @@ -76,10 +76,10 @@ ## 3.0.0-beta.14 - 2021-11-30 ### Changed -* Guarantee ordering of `header::GetAll` iterator to be same as insertion order. [#2467] -* Expose `header::map` module. [#2467] -* Implement `ExactSizeIterator` and `FusedIterator` for all `HeaderMap` iterators. [#2470] -* Update `actix-tls` to `3.0.0-rc.1`. [#2474] +- Guarantee ordering of `header::GetAll` iterator to be same as insertion order. [#2467] +- Expose `header::map` module. [#2467] +- Implement `ExactSizeIterator` and `FusedIterator` for all `HeaderMap` iterators. [#2470] +- Update `actix-tls` to `3.0.0-rc.1`. [#2474] [#2467]: https://github.com/actix/actix-web/pull/2467 [#2470]: https://github.com/actix/actix-web/pull/2470 @@ -88,24 +88,24 @@ ## 3.0.0-beta.13 - 2021-11-22 ### Added -* `body::AnyBody::empty` for quickly creating an empty body. [#2446] -* `body::AnyBody::none` for quickly creating a "none" body. [#2456] -* `impl Clone` for `body::AnyBody where S: Clone`. [#2448] -* `body::AnyBody::into_boxed` for quickly converting to a type-erased, boxed body type. [#2448] +- `body::AnyBody::empty` for quickly creating an empty body. [#2446] +- `body::AnyBody::none` for quickly creating a "none" body. [#2456] +- `impl Clone` for `body::AnyBody where S: Clone`. [#2448] +- `body::AnyBody::into_boxed` for quickly converting to a type-erased, boxed body type. [#2448] ### Changed -* Rename `body::AnyBody::{Message => Body}`. [#2446] -* Rename `body::AnyBody::{from_message => new_boxed}`. [#2448] -* Rename `body::AnyBody::{from_slice => copy_from_slice}`. [#2448] -* Rename `body::{BoxAnyBody => BoxBody}`. [#2448] -* Change representation of `AnyBody` to include a type parameter in `Body` variant. Defaults to `BoxBody`. [#2448] -* `Encoder::response` now returns `AnyBody>`. [#2448] +- Rename `body::AnyBody::{Message => Body}`. [#2446] +- Rename `body::AnyBody::{from_message => new_boxed}`. [#2448] +- Rename `body::AnyBody::{from_slice => copy_from_slice}`. [#2448] +- Rename `body::{BoxAnyBody => BoxBody}`. [#2448] +- Change representation of `AnyBody` to include a type parameter in `Body` variant. Defaults to `BoxBody`. [#2448] +- `Encoder::response` now returns `AnyBody>`. [#2448] ### Removed -* `body::AnyBody::Empty`; an empty body can now only be represented as a zero-length `Bytes` variant. [#2446] -* `body::BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446] -* `EncoderError::Boxed`; it is no longer required. [#2446] -* `body::ResponseBody`; is function is replaced by the new `body::AnyBody` enum. [#2446] +- `body::AnyBody::Empty`; an empty body can now only be represented as a zero-length `Bytes` variant. [#2446] +- `body::BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446] +- `EncoderError::Boxed`; it is no longer required. [#2446] +- `body::ResponseBody`; is function is replaced by the new `body::AnyBody` enum. [#2446] [#2446]: https://github.com/actix/actix-web/pull/2446 [#2448]: https://github.com/actix/actix-web/pull/2448 @@ -114,11 +114,11 @@ ## 3.0.0-beta.12 - 2021-11-15 ### Changed -* Update `actix-server` to `2.0.0-beta.9`. [#2442] +- Update `actix-server` to `2.0.0-beta.9`. [#2442] ### Removed -* `client` module. [#2425] -* `trust-dns` feature. [#2425] +- `client` module. [#2425] +- `trust-dns` feature. [#2425] [#2425]: https://github.com/actix/actix-web/pull/2425 [#2442]: https://github.com/actix/actix-web/pull/2442 @@ -126,21 +126,21 @@ ## 3.0.0-beta.11 - 2021-10-20 ### Changed -* Updated rustls to v0.20. [#2414] -* Minimum supported Rust version (MSRV) is now 1.52. +- Updated rustls to v0.20. [#2414] +- Minimum supported Rust version (MSRV) is now 1.52. [#2414]: https://github.com/actix/actix-web/pull/2414 ## 3.0.0-beta.10 - 2021-09-09 ### Changed -* `ContentEncoding` is now marked `#[non_exhaustive]`. [#2377] -* Minimum supported Rust version (MSRV) is now 1.51. +- `ContentEncoding` is now marked `#[non_exhaustive]`. [#2377] +- Minimum supported Rust version (MSRV) is now 1.51. ### Fixed -* Remove slice creation pointing to potential uninitialized data on h1 encoder. [#2364] -* Remove `Into` bound on `Encoder` body types. [#2375] -* Fix quality parse error in Accept-Encoding header. [#2344] +- Remove slice creation pointing to potential uninitialized data on h1 encoder. [#2364] +- Remove `Into` bound on `Encoder` body types. [#2375] +- Fix quality parse error in Accept-Encoding header. [#2344] [#2364]: https://github.com/actix/actix-web/pull/2364 [#2375]: https://github.com/actix/actix-web/pull/2375 @@ -150,15 +150,15 @@ ## 3.0.0-beta.9 - 2021-08-09 ### Fixed -* Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977) +- Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977) ## 3.0.0-beta.8 - 2021-06-26 ### Changed -* Change compression algorithm features flags. [#2250] +- Change compression algorithm features flags. [#2250] ### Removed -* `downcast` and `downcast_get_type_id` macros. [#2291] +- `downcast` and `downcast_get_type_id` macros. [#2291] [#2291]: https://github.com/actix/actix-web/pull/2291 [#2250]: https://github.com/actix/actix-web/pull/2250 @@ -166,37 +166,37 @@ ## 3.0.0-beta.7 - 2021-06-17 ### Added -* Alias `body::Body` as `body::AnyBody`. [#2215] -* `BoxAnyBody`: a boxed message body with boxed errors. [#2183] -* Re-export `http` crate's `Error` type as `error::HttpError`. [#2171] -* Re-export `StatusCode`, `Method`, `Version` and `Uri` at the crate root. [#2171] -* Re-export `ContentEncoding` and `ConnectionType` at the crate root. [#2171] -* `Response::into_body` that consumes response and returns body type. [#2201] -* `impl Default` for `Response`. [#2201] -* Add zstd support for `ContentEncoding`. [#2244] +- Alias `body::Body` as `body::AnyBody`. [#2215] +- `BoxAnyBody`: a boxed message body with boxed errors. [#2183] +- Re-export `http` crate's `Error` type as `error::HttpError`. [#2171] +- Re-export `StatusCode`, `Method`, `Version` and `Uri` at the crate root. [#2171] +- Re-export `ContentEncoding` and `ConnectionType` at the crate root. [#2171] +- `Response::into_body` that consumes response and returns body type. [#2201] +- `impl Default` for `Response`. [#2201] +- Add zstd support for `ContentEncoding`. [#2244] ### Changed -* The `MessageBody` trait now has an associated `Error` type. [#2183] -* All error trait bounds in server service builders have changed from `Into` to `Into>`. [#2253] -* All error trait bounds in message body and stream impls changed from `Into` to `Into>`. [#2253] -* Places in `Response` where `ResponseBody` was received or returned now simply use `B`. [#2201] -* `header` mod is now public. [#2171] -* `uri` mod is now public. [#2171] -* Update `language-tags` to `0.3`. -* Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201] -* `ResponseBuilder::message_body` now returns a `Result`. [#2201] -* Remove `Unpin` bound on `ResponseBuilder::streaming`. [#2253] -* `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226] +- The `MessageBody` trait now has an associated `Error` type. [#2183] +- All error trait bounds in server service builders have changed from `Into` to `Into>`. [#2253] +- All error trait bounds in message body and stream impls changed from `Into` to `Into>`. [#2253] +- Places in `Response` where `ResponseBody` was received or returned now simply use `B`. [#2201] +- `header` mod is now public. [#2171] +- `uri` mod is now public. [#2171] +- Update `language-tags` to `0.3`. +- Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201] +- `ResponseBuilder::message_body` now returns a `Result`. [#2201] +- Remove `Unpin` bound on `ResponseBuilder::streaming`. [#2253] +- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226] ### Removed -* Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171] -* Down-casting for `MessageBody` types. [#2183] -* `error::Result` alias. [#2201] -* Error field from `Response` and `Response::error`. [#2205] -* `impl Future` for `Response`. [#2201] -* `Response::take_body` and old `Response::into_body` method that casted body type. [#2201] -* `InternalError` and all the error types it constructed. [#2215] -* Conversion (`impl Into`) of `Response` and `ResponseBuilder` to `Error`. [#2215] +- Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171] +- Down-casting for `MessageBody` types. [#2183] +- `error::Result` alias. [#2201] +- Error field from `Response` and `Response::error`. [#2205] +- `impl Future` for `Response`. [#2201] +- `Response::take_body` and old `Response::into_body` method that casted body type. [#2201] +- `InternalError` and all the error types it constructed. [#2215] +- Conversion (`impl Into`) of `Response` and `ResponseBuilder` to `Error`. [#2215] [#2171]: https://github.com/actix/actix-web/pull/2171 [#2183]: https://github.com/actix/actix-web/pull/2183 @@ -211,27 +211,27 @@ ## 3.0.0-beta.6 - 2021-04-17 ### Added -* `impl MessageBody for Pin>`. [#2152] -* `Response::{ok, bad_request, not_found, internal_server_error}`. [#2159] -* Helper `body::to_bytes` for async collecting message body into Bytes. [#2158] +- `impl MessageBody for Pin>`. [#2152] +- `Response::{ok, bad_request, not_found, internal_server_error}`. [#2159] +- Helper `body::to_bytes` for async collecting message body into Bytes. [#2158] ### Changes -* The type parameter of `Response` no longer has a default. [#2152] -* The `Message` variant of `body::Body` is now `Pin>`. [#2152] -* `BodyStream` and `SizedStream` are no longer restricted to Unpin types. [#2152] -* Error enum types are marked `#[non_exhaustive]`. [#2161] +- The type parameter of `Response` no longer has a default. [#2152] +- The `Message` variant of `body::Body` is now `Pin>`. [#2152] +- `BodyStream` and `SizedStream` are no longer restricted to Unpin types. [#2152] +- Error enum types are marked `#[non_exhaustive]`. [#2161] ### Removed -* `cookies` feature flag. [#2065] -* Top-level `cookies` mod (re-export). [#2065] -* `HttpMessage` trait loses the `cookies` and `cookie` methods. [#2065] -* `impl ResponseError for CookieParseError`. [#2065] -* Deprecated methods on `ResponseBuilder`: `if_true`, `if_some`. [#2148] -* `ResponseBuilder::json`. [#2148] -* `ResponseBuilder::{set_header, header}`. [#2148] -* `impl From for Body`. [#2148] -* `Response::build_from`. [#2159] -* Most of the status code builders on `Response`. [#2159] +- `cookies` feature flag. [#2065] +- Top-level `cookies` mod (re-export). [#2065] +- `HttpMessage` trait loses the `cookies` and `cookie` methods. [#2065] +- `impl ResponseError for CookieParseError`. [#2065] +- Deprecated methods on `ResponseBuilder`: `if_true`, `if_some`. [#2148] +- `ResponseBuilder::json`. [#2148] +- `ResponseBuilder::{set_header, header}`. [#2148] +- `impl From for Body`. [#2148] +- `Response::build_from`. [#2159] +- Most of the status code builders on `Response`. [#2159] [#2065]: https://github.com/actix/actix-web/pull/2065 [#2148]: https://github.com/actix/actix-web/pull/2148 @@ -243,16 +243,16 @@ ## 3.0.0-beta.5 - 2021-04-02 ### Added -* `client::Connector::handshake_timeout` method for customizing TLS connection handshake timeout. [#2081] -* `client::ConnectorService` as `client::Connector::finish` method's return type [#2081] -* `client::ConnectionIo` trait alias [#2081] +- `client::Connector::handshake_timeout` method for customizing TLS connection handshake timeout. [#2081] +- `client::ConnectorService` as `client::Connector::finish` method's return type [#2081] +- `client::ConnectionIo` trait alias [#2081] ### Changed -* `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063] +- `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063] ### Removed -* Common typed HTTP headers were moved to actix-web. [2094] -* `ResponseError` impl for `actix_utils::timeout::TimeoutError`. [#2127] +- Common typed HTTP headers were moved to actix-web. [2094] +- `ResponseError` impl for `actix_utils::timeout::TimeoutError`. [#2127] [#2063]: https://github.com/actix/actix-web/pull/2063 [#2081]: https://github.com/actix/actix-web/pull/2081 @@ -262,13 +262,13 @@ ## 3.0.0-beta.4 - 2021-03-08 ### Changed -* Feature `cookies` is now optional and disabled by default. [#1981] -* `ws::hash_key` now returns array. [#2035] -* `ResponseBuilder::json` now takes `impl Serialize`. [#2052] +- Feature `cookies` is now optional and disabled by default. [#1981] +- `ws::hash_key` now returns array. [#2035] +- `ResponseBuilder::json` now takes `impl Serialize`. [#2052] ### Removed -* Re-export of `futures_channel::oneshot::Canceled` is removed from `error` mod. [#1994] -* `ResponseError` impl for `futures_channel::oneshot::Canceled` is removed. [#1994] +- Re-export of `futures_channel::oneshot::Canceled` is removed from `error` mod. [#1994] +- `ResponseError` impl for `futures_channel::oneshot::Canceled` is removed. [#1994] [#1981]: https://github.com/actix/actix-web/pull/1981 [#1994]: https://github.com/actix/actix-web/pull/1994 @@ -277,48 +277,48 @@ ## 3.0.0-beta.3 - 2021-02-10 -* No notable changes. +- No notable changes. ## 3.0.0-beta.2 - 2021-02-10 ### Added -* `TryIntoHeaderPair` trait that allows using typed and untyped headers in the same methods. [#1869] -* `ResponseBuilder::insert_header` method which allows using typed headers. [#1869] -* `ResponseBuilder::append_header` method which allows using typed headers. [#1869] -* `TestRequest::insert_header` method which allows using typed headers. [#1869] -* `ContentEncoding` implements all necessary header traits. [#1912] -* `HeaderMap::len_keys` has the behavior of the old `len` method. [#1964] -* `HeaderMap::drain` as an efficient draining iterator. [#1964] -* Implement `IntoIterator` for owned `HeaderMap`. [#1964] -* `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969] +- `TryIntoHeaderPair` trait that allows using typed and untyped headers in the same methods. [#1869] +- `ResponseBuilder::insert_header` method which allows using typed headers. [#1869] +- `ResponseBuilder::append_header` method which allows using typed headers. [#1869] +- `TestRequest::insert_header` method which allows using typed headers. [#1869] +- `ContentEncoding` implements all necessary header traits. [#1912] +- `HeaderMap::len_keys` has the behavior of the old `len` method. [#1964] +- `HeaderMap::drain` as an efficient draining iterator. [#1964] +- Implement `IntoIterator` for owned `HeaderMap`. [#1964] +- `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969] ### Changed -* `ResponseBuilder::content_type` now takes an `impl TryIntoHeaderValue` to support using typed +- `ResponseBuilder::content_type` now takes an `impl TryIntoHeaderValue` to support using typed `mime` types. [#1894] -* Renamed `TryIntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std +- Renamed `TryIntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std `TryInto` trait. [#1894] -* `Extensions::insert` returns Option of replaced item. [#1904] -* Remove `HttpResponseBuilder::json2()`. [#1903] -* Enable `HttpResponseBuilder::json()` to receive data by value and reference. [#1903] -* `client::error::ConnectError` Resolver variant contains `Box` type. [#1905] -* `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905] -* Simplify `BlockingError` type to a unit struct. It's now only triggered when blocking thread pool +- `Extensions::insert` returns Option of replaced item. [#1904] +- Remove `HttpResponseBuilder::json2()`. [#1903] +- Enable `HttpResponseBuilder::json()` to receive data by value and reference. [#1903] +- `client::error::ConnectError` Resolver variant contains `Box` type. [#1905] +- `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905] +- Simplify `BlockingError` type to a unit struct. It's now only triggered when blocking thread pool is dead. [#1957] -* `HeaderMap::len` now returns number of values instead of number of keys. [#1964] -* `HeaderMap::insert` now returns iterator of removed values. [#1964] -* `HeaderMap::remove` now returns iterator of removed values. [#1964] +- `HeaderMap::len` now returns number of values instead of number of keys. [#1964] +- `HeaderMap::insert` now returns iterator of removed values. [#1964] +- `HeaderMap::remove` now returns iterator of removed values. [#1964] ### Removed -* `ResponseBuilder::set`; use `ResponseBuilder::insert_header`. [#1869] -* `ResponseBuilder::set_header`; use `ResponseBuilder::insert_header`. [#1869] -* `ResponseBuilder::header`; use `ResponseBuilder::append_header`. [#1869] -* `TestRequest::with_hdr`; use `TestRequest::default().insert_header()`. [#1869] -* `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869] -* `actors` optional feature. [#1969] -* `ResponseError` impl for `actix::MailboxError`. [#1969] +- `ResponseBuilder::set`; use `ResponseBuilder::insert_header`. [#1869] +- `ResponseBuilder::set_header`; use `ResponseBuilder::insert_header`. [#1869] +- `ResponseBuilder::header`; use `ResponseBuilder::append_header`. [#1869] +- `TestRequest::with_hdr`; use `TestRequest::default().insert_header()`. [#1869] +- `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869] +- `actors` optional feature. [#1969] +- `ResponseError` impl for `actix::MailboxError`. [#1969] ### Documentation -* Vastly improve docs and add examples for `HeaderMap`. [#1964] +- Vastly improve docs and add examples for `HeaderMap`. [#1964] [#1869]: https://github.com/actix/actix-web/pull/1869 [#1894]: https://github.com/actix/actix-web/pull/1894 @@ -333,24 +333,24 @@ ## 3.0.0-beta.1 - 2021-01-07 ### Added -* Add `Http3` to `Protocol` enum for future compatibility and also mark `#[non_exhaustive]`. +- Add `Http3` to `Protocol` enum for future compatibility and also mark `#[non_exhaustive]`. ### Changed -* Update `actix-*` dependencies to tokio `1.0` based versions. [#1813] -* Bumped `rand` to `0.8`. -* Update `bytes` to `1.0`. [#1813] -* Update `h2` to `0.3`. [#1813] -* The `ws::Message::Text` enum variant now contains a `bytestring::ByteString`. [#1864] +- Update `actix-*` dependencies to tokio `1.0` based versions. [#1813] +- Bumped `rand` to `0.8`. +- Update `bytes` to `1.0`. [#1813] +- Update `h2` to `0.3`. [#1813] +- The `ws::Message::Text` enum variant now contains a `bytestring::ByteString`. [#1864] ### Removed -* Deprecated `on_connect` methods have been removed. Prefer the new +- Deprecated `on_connect` methods have been removed. Prefer the new `on_connect_ext` technique. [#1857] -* Remove `ResponseError` impl for `actix::actors::resolver::ResolverError` +- Remove `ResponseError` impl for `actix::actors::resolver::ResolverError` due to deprecate of resolver actor. [#1813] -* Remove `ConnectError::SslHandshakeError` and re-export of `HandshakeError`. +- Remove `ConnectError::SslHandshakeError` and re-export of `HandshakeError`. due to the removal of this type from `tokio-openssl` crate. openssl handshake error would return as `ConnectError::SslError`. [#1813] -* Remove `actix-threadpool` dependency. Use `actix_rt::task::spawn_blocking`. +- Remove `actix-threadpool` dependency. Use `actix_rt::task::spawn_blocking`. Due to this change `actix_threadpool::BlockingError` type is moved into `actix_http::error` module. [#1878] @@ -362,20 +362,20 @@ ## 2.2.1 - 2021-08-09 ### Fixed -* Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977) +- Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977) ## 2.2.0 - 2020-11-25 ### Added -* HttpResponse builders for 1xx status codes. [#1768] -* `Accept::mime_precedence` and `Accept::mime_preference`. [#1793] -* `TryFrom` and `TryFrom` for `http::header::Quality`. [#1797] +- HttpResponse builders for 1xx status codes. [#1768] +- `Accept::mime_precedence` and `Accept::mime_preference`. [#1793] +- `TryFrom` and `TryFrom` for `http::header::Quality`. [#1797] ### Fixed -* Started dropping `transfer-encoding: chunked` and `Content-Length` for 1XX and 204 responses. [#1767] +- Started dropping `transfer-encoding: chunked` and `Content-Length` for 1XX and 204 responses. [#1767] ### Changed -* Upgrade `serde_urlencoded` to `0.7`. [#1773] +- Upgrade `serde_urlencoded` to `0.7`. [#1773] [#1773]: https://github.com/actix/actix-web/pull/1773 [#1767]: https://github.com/actix/actix-web/pull/1767 @@ -386,12 +386,12 @@ ## 2.1.0 - 2020-10-30 ### Added -* Added more flexible `on_connect_ext` methods for on-connect handling. [#1754] +- Added more flexible `on_connect_ext` methods for on-connect handling. [#1754] ### Changed -* Upgrade `base64` to `0.13`. [#1744] -* Upgrade `pin-project` to `1.0`. [#1733] -* Deprecate `ResponseBuilder::{if_some, if_true}`. [#1760] +- Upgrade `base64` to `0.13`. [#1744] +- Upgrade `pin-project` to `1.0`. [#1733] +- Deprecate `ResponseBuilder::{if_some, if_true}`. [#1760] [#1760]: https://github.com/actix/actix-web/pull/1760 [#1754]: https://github.com/actix/actix-web/pull/1754 @@ -400,28 +400,28 @@ ## 2.0.0 - 2020-09-11 -* No significant changes from `2.0.0-beta.4`. +- No significant changes from `2.0.0-beta.4`. ## 2.0.0-beta.4 - 2020-09-09 ### Changed -* Update actix-codec and actix-utils dependencies. -* Update actix-connect and actix-tls dependencies. +- Update actix-codec and actix-utils dependencies. +- Update actix-connect and actix-tls dependencies. ## 2.0.0-beta.3 - 2020-08-14 ### Fixed -* Memory leak of `client::pool::ConnectorPoolSupport`. [#1626] +- Memory leak of `client::pool::ConnectorPoolSupport`. [#1626] [#1626]: https://github.com/actix/actix-web/pull/1626 ## 2.0.0-beta.2 - 2020-07-21 ### Fixed -* Potential UB in h1 decoder using uninitialized memory. [#1614] +- Potential UB in h1 decoder using uninitialized memory. [#1614] ### Changed -* Fix illegal chunked encoding. [#1615] +- Fix illegal chunked encoding. [#1615] [#1614]: https://github.com/actix/actix-web/pull/1614 [#1615]: https://github.com/actix/actix-web/pull/1615 @@ -429,10 +429,10 @@ ## 2.0.0-beta.1 - 2020-07-11 ### Changed -* Migrate cookie handling to `cookie` crate. [#1558] -* Update `sha-1` to 0.9. [#1586] -* Fix leak in client pool. [#1580] -* MSRV is now 1.41.1. +- Migrate cookie handling to `cookie` crate. [#1558] +- Update `sha-1` to 0.9. [#1586] +- Fix leak in client pool. [#1580] +- MSRV is now 1.41.1. [#1558]: https://github.com/actix/actix-web/pull/1558 [#1586]: https://github.com/actix/actix-web/pull/1586 @@ -441,15 +441,15 @@ ## 2.0.0-alpha.4 - 2020-05-21 ### Changed -* Bump minimum supported Rust version to 1.40 -* content_length function is removed, and you can set Content-Length by calling +- Bump minimum supported Rust version to 1.40 +- content_length function is removed, and you can set Content-Length by calling no_chunking function [#1439] -* `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a +- `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a `u64` instead of a `usize`. -* Update `base64` dependency to 0.12 +- Update `base64` dependency to 0.12 ### Fixed -* Support parsing of `SameSite=None` [#1503] +- Support parsing of `SameSite=None` [#1503] [#1439]: https://github.com/actix/actix-web/pull/1439 [#1503]: https://github.com/actix/actix-web/pull/1503 @@ -457,13 +457,13 @@ ## 2.0.0-alpha.3 - 2020-05-08 ### Fixed -* Correct spelling of ConnectError::Unresolved [#1487] -* Fix a mistake in the encoding of websocket continuation messages wherein +- Correct spelling of ConnectError::Unresolved [#1487] +- Fix a mistake in the encoding of websocket continuation messages wherein Item::FirstText and Item::FirstBinary are each encoded as the other. ### Changed -* Implement `std::error::Error` for our custom errors [#1422] -* Remove `failure` support for `ResponseError` since that crate +- Implement `std::error::Error` for our custom errors [#1422] +- Remove `failure` support for `ResponseError` since that crate will be deprecated in the near future. [#1422]: https://github.com/actix/actix-web/pull/1422 @@ -472,12 +472,12 @@ ## 2.0.0-alpha.2 - 2020-03-07 ### Changed -* Update `actix-connect` and `actix-tls` dependency to 2.0.0-alpha.1. [#1395] -* Change default initial window size and connection window size for HTTP2 to 2MB and 1MB +- Update `actix-connect` and `actix-tls` dependency to 2.0.0-alpha.1. [#1395] +- Change default initial window size and connection window size for HTTP2 to 2MB and 1MB respectively to improve download speed for awc when downloading large objects. [#1394] -* client::Connector accepts initial_window_size and initial_connection_window_size +- client::Connector accepts initial_window_size and initial_connection_window_size HTTP2 configuration. [#1394] -* client::Connector allowing to set max_http_version to limit HTTP version to be used. [#1394] +- client::Connector allowing to set max_http_version to limit HTTP version to be used. [#1394] [#1394]: https://github.com/actix/actix-web/pull/1394 [#1395]: https://github.com/actix/actix-web/pull/1395 @@ -485,61 +485,61 @@ ## 2.0.0-alpha.1 - 2020-02-27 ### Changed -* Update the `time` dependency to 0.2.7. -* Moved actors messages support from actix crate, enabled with feature `actors`. -* Breaking change: trait MessageBody requires Unpin and accepting `Pin<&mut Self>` instead of +- Update the `time` dependency to 0.2.7. +- Moved actors messages support from actix crate, enabled with feature `actors`. +- Breaking change: trait MessageBody requires Unpin and accepting `Pin<&mut Self>` instead of `&mut self` in the poll_next(). -* MessageBody is not implemented for &'static [u8] anymore. +- MessageBody is not implemented for &'static [u8] anymore. ### Fixed -* Allow `SameSite=None` cookies to be sent in a response. +- Allow `SameSite=None` cookies to be sent in a response. ## 1.0.1 - 2019-12-20 ### Fixed -* Poll upgrade service's readiness from HTTP service handlers -* Replace brotli with brotli2 #1224 +- Poll upgrade service's readiness from HTTP service handlers +- Replace brotli with brotli2 #1224 ## 1.0.0 - 2019-12-13 ### Added -* Add websockets continuation frame support +- Add websockets continuation frame support ### Changed -* Replace `flate2-xxx` features with `compress` +- Replace `flate2-xxx` features with `compress` ## 1.0.0-alpha.5 - 2019-12-09 ### Fixed -* Check `Upgrade` service readiness before calling it -* Fix buffer remaining capacity calculation +- Check `Upgrade` service readiness before calling it +- Fix buffer remaining capacity calculation ### Changed -* Websockets: Ping and Pong should have binary data #1049 +- Websockets: Ping and Pong should have binary data #1049 ## 1.0.0-alpha.4 - 2019-12-08 ### Added -* Add impl ResponseBuilder for Error +- Add impl ResponseBuilder for Error ### Changed -* Use rust based brotli compression library +- Use rust based brotli compression library ## 1.0.0-alpha.3 - 2019-12-07 ### Changed -* Migrate to tokio 0.2 -* Migrate to `std::future` +- Migrate to tokio 0.2 +- Migrate to `std::future` ## 0.2.11 - 2019-11-06 ### Added -* Add support for serde_json::Value to be passed as argument to ResponseBuilder.body() -* Add an additional `filename*` param in the `Content-Disposition` header of +- Add support for serde_json::Value to be passed as argument to ResponseBuilder.body() +- Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151) -* Allow to use `std::convert::Infallible` as `actix_http::error::Error` +- Allow to use `std::convert::Infallible` as `actix_http::error::Error` ### Fixed -* To be compatible with non-English error responses, `ResponseError` rendered with `text/plain; +- To be compatible with non-English error responses, `ResponseError` rendered with `text/plain; charset=utf-8` header [#1118] [#1878]: https://github.com/actix/actix-web/pull/1878 @@ -547,169 +547,169 @@ ## 0.2.10 - 2019-09-11 ### Added -* Add support for sending HTTP requests with `Rc` in addition to sending HTTP requests +- Add support for sending HTTP requests with `Rc` in addition to sending HTTP requests with `RequestHead` ### Fixed -* h2 will use error response #1080 -* on_connect result isn't added to request extensions for http2 requests #1009 +- h2 will use error response #1080 +- on_connect result isn't added to request extensions for http2 requests #1009 ## 0.2.9 - 2019-08-13 ### Changed -* Dropped the `byteorder`-dependency in favor of `stdlib`-implementation -* Update percent-encoding to 2.1 -* Update serde_urlencoded to 0.6.1 +- Dropped the `byteorder`-dependency in favor of `stdlib`-implementation +- Update percent-encoding to 2.1 +- Update serde_urlencoded to 0.6.1 ### Fixed -* Fixed a panic in the HTTP2 handshake in client HTTP requests (#1031) +- Fixed a panic in the HTTP2 handshake in client HTTP requests (#1031) ## 0.2.8 - 2019-08-01 ### Added -* Add `rustls` support -* Add `Clone` impl for `HeaderMap` +- Add `rustls` support +- Add `Clone` impl for `HeaderMap` ### Fixed -* awc client panic #1016 -* Invalid response with compression middleware enabled, but compression-related features +- awc client panic #1016 +- Invalid response with compression middleware enabled, but compression-related features disabled #997 ## 0.2.7 - 2019-07-18 ### Added -* Add support for downcasting response errors #986 +- Add support for downcasting response errors #986 ## 0.2.6 - 2019-07-17 ### Changed -* Replace `ClonableService` with local copy -* Upgrade `rand` dependency version to 0.7 +- Replace `ClonableService` with local copy +- Upgrade `rand` dependency version to 0.7 ## 0.2.5 - 2019-06-28 ### Added -* Add `on-connect` callback, `HttpServiceBuilder::on_connect()` #946 +- Add `on-connect` callback, `HttpServiceBuilder::on_connect()` #946 ### Changed -* Use `encoding_rs` crate instead of unmaintained `encoding` crate -* Add `Copy` and `Clone` impls for `ws::Codec` +- Use `encoding_rs` crate instead of unmaintained `encoding` crate +- Add `Copy` and `Clone` impls for `ws::Codec` ## 0.2.4 - 2019-06-16 ### Fixed -* Do not compress NoContent (204) responses #918 +- Do not compress NoContent (204) responses #918 ## 0.2.3 - 2019-06-02 ### Added -* Debug impl for ResponseBuilder -* From SizedStream and BodyStream for Body +- Debug impl for ResponseBuilder +- From SizedStream and BodyStream for Body ### Changed -* SizedStream uses u64 +- SizedStream uses u64 ## 0.2.2 - 2019-05-29 ### Fixed -* Parse incoming stream before closing stream on disconnect #868 +- Parse incoming stream before closing stream on disconnect #868 ## 0.2.1 - 2019-05-25 ### Fixed -* Handle socket read disconnect +- Handle socket read disconnect ## 0.2.0 - 2019-05-12 ### Changed -* Update actix-service to 0.4 -* Expect and upgrade services accept `ServerConfig` config. +- Update actix-service to 0.4 +- Expect and upgrade services accept `ServerConfig` config. ### Deleted -* `OneRequest` service +- `OneRequest` service ## 0.1.5 - 2019-05-04 ### Fixed -* Clean up response extensions in response pool #817 +- Clean up response extensions in response pool #817 ## 0.1.4 - 2019-04-24 ### Added -* Allow to render h1 request headers in `Camel-Case` +- Allow to render h1 request headers in `Camel-Case` ### Fixed -* Read until eof for http/1.0 responses #771 +- Read until eof for http/1.0 responses #771 ## 0.1.3 - 2019-04-23 ### Fixed -* Fix http client pool management -* Fix http client wait queue management #794 +- Fix http client pool management +- Fix http client wait queue management #794 ## 0.1.2 - 2019-04-23 ### Fixed -* Fix BorrowMutError panic in client connector #793 +- Fix BorrowMutError panic in client connector #793 ## 0.1.1 - 2019-04-19 ### Changed -* Cookie::max_age() accepts value in seconds -* Cookie::max_age_time() accepts value in time::Duration -* Allow to specify server address for client connector +- Cookie::max_age() accepts value in seconds +- Cookie::max_age_time() accepts value in time::Duration +- Allow to specify server address for client connector ## 0.1.0 - 2019-04-16 ### Added -* Expose peer addr via `Request::peer_addr()` and `RequestHead::peer_addr` +- Expose peer addr via `Request::peer_addr()` and `RequestHead::peer_addr` ### Changed -* `actix_http::encoding` always available -* use trust-dns-resolver 0.11.0 +- `actix_http::encoding` always available +- use trust-dns-resolver 0.11.0 ## 0.1.0-alpha.5 - 2019-04-12 ### Added -* Allow to use custom service for upgrade requests -* Added `h1::SendResponse` future. +- Allow to use custom service for upgrade requests +- Added `h1::SendResponse` future. ### Changed -* MessageBody::length() renamed to MessageBody::size() for consistency -* ws handshake verification functions take RequestHead instead of Request +- MessageBody::length() renamed to MessageBody::size() for consistency +- ws handshake verification functions take RequestHead instead of Request ## 0.1.0-alpha.4 - 2019-04-08 ### Added -* Allow to use custom `Expect` handler -* Add minimal `std::error::Error` impl for `Error` +- Allow to use custom `Expect` handler +- Add minimal `std::error::Error` impl for `Error` ### Changed -* Export IntoHeaderValue -* Render error and return as response body -* Use thread pool for response body compression +- Export IntoHeaderValue +- Render error and return as response body +- Use thread pool for response body compression ### Deleted -* Removed PayloadBuffer +- Removed PayloadBuffer ## 0.1.0-alpha.3 - 2019-04-02 ### Added -* Warn when an unsealed private cookie isn't valid UTF-8 +- Warn when an unsealed private cookie isn't valid UTF-8 ### Fixed -* Rust 1.31.0 compatibility -* Preallocate read buffer for h1 codec -* Detect socket disconnection during protocol selection +- Rust 1.31.0 compatibility +- Preallocate read buffer for h1 codec +- Detect socket disconnection during protocol selection ## 0.1.0-alpha.2 - 2019-03-29 ### Added -* Added ws::Message::Nop, no-op websockets message +- Added ws::Message::Nop, no-op websockets message ### Changed -* Do not use thread pool for decompression if chunk size is smaller than 2048. +- Do not use thread pool for decompression if chunk size is smaller than 2048. ## 0.1.0-alpha.1 - 2019-03-28 -* Initial impl +- Initial impl diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index 8d9c1640f..e58c3ee24 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -4,119 +4,119 @@ ## 0.4.0-beta.10 - 2021-12-11 -* No significant changes since `0.4.0-beta.9`. +- No significant changes since `0.4.0-beta.9`. ## 0.4.0-beta.9 - 2021-12-01 -* Polling `Field` after dropping `Multipart` now fails immediately instead of hanging forever. [#2463] +- Polling `Field` after dropping `Multipart` now fails immediately instead of hanging forever. [#2463] [#2463]: https://github.com/actix/actix-web/pull/2463 ## 0.4.0-beta.8 - 2021-11-22 -* Ensure a correct Content-Disposition header is included in every part of a multipart message. [#2451] -* Added `MultipartError::NoContentDisposition` variant. [#2451] -* Since Content-Disposition is now ensured, `Field::content_disposition` is now infallible. [#2451] -* Added `Field::name` method for getting the field name. [#2451] -* `MultipartError` now marks variants with inner errors as the source. [#2451] -* `MultipartError` is now marked as non-exhaustive. [#2451] +- Ensure a correct Content-Disposition header is included in every part of a multipart message. [#2451] +- Added `MultipartError::NoContentDisposition` variant. [#2451] +- Since Content-Disposition is now ensured, `Field::content_disposition` is now infallible. [#2451] +- Added `Field::name` method for getting the field name. [#2451] +- `MultipartError` now marks variants with inner errors as the source. [#2451] +- `MultipartError` is now marked as non-exhaustive. [#2451] [#2451]: https://github.com/actix/actix-web/pull/2451 ## 0.4.0-beta.7 - 2021-10-20 -* Minimum supported Rust version (MSRV) is now 1.52. +- Minimum supported Rust version (MSRV) is now 1.52. ## 0.4.0-beta.6 - 2021-09-09 -* Minimum supported Rust version (MSRV) is now 1.51. +- Minimum supported Rust version (MSRV) is now 1.51. ## 0.4.0-beta.5 - 2021-06-17 -* No notable changes. +- No notable changes. ## 0.4.0-beta.4 - 2021-04-02 -* No notable changes. +- No notable changes. ## 0.4.0-beta.3 - 2021-03-09 -* No notable changes. +- No notable changes. ## 0.4.0-beta.2 - 2021-02-10 -* No notable changes. +- No notable changes. ## 0.4.0-beta.1 - 2021-01-07 -* Fix multipart consuming payload before header checks. [#1513] -* Update `bytes` to `1.0`. [#1813] +- Fix multipart consuming payload before header checks. [#1513] +- Update `bytes` to `1.0`. [#1813] [#1813]: https://github.com/actix/actix-web/pull/1813 [#1513]: https://github.com/actix/actix-web/pull/1513 ## 0.3.0 - 2020-09-11 -* No significant changes from `0.3.0-beta.2`. +- No significant changes from `0.3.0-beta.2`. ## 0.3.0-beta.2 - 2020-09-10 -* Update `actix-*` dependencies to latest versions. +- Update `actix-*` dependencies to latest versions. ## 0.3.0-beta.1 - 2020-07-15 -* Update `actix-web` to 3.0.0-beta.1 +- Update `actix-web` to 3.0.0-beta.1 ## 0.3.0-alpha.1 - 2020-05-25 -* Update `actix-web` to 3.0.0-alpha.3 -* Bump minimum supported Rust version to 1.40 -* Minimize `futures` dependencies -* Remove the unused `time` dependency -* Fix missing `std::error::Error` implement for `MultipartError`. +- Update `actix-web` to 3.0.0-alpha.3 +- Bump minimum supported Rust version to 1.40 +- Minimize `futures` dependencies +- Remove the unused `time` dependency +- Fix missing `std::error::Error` implement for `MultipartError`. ## [0.2.0] - 2019-12-20 -* Release +- Release ## [0.2.0-alpha.4] - 2019-12-xx -* Multipart handling now handles Pending during read of boundary #1205 +- Multipart handling now handles Pending during read of boundary #1205 ## [0.2.0-alpha.2] - 2019-12-03 -* Migrate to `std::future` +- Migrate to `std::future` ## [0.1.4] - 2019-09-12 -* Multipart handling now parses requests which do not end in CRLF #1038 +- Multipart handling now parses requests which do not end in CRLF #1038 ## [0.1.3] - 2019-08-18 -* Fix ring dependency from actix-web default features for #741. +- Fix ring dependency from actix-web default features for #741. ## [0.1.2] - 2019-06-02 -* Fix boundary parsing #876 +- Fix boundary parsing #876 ## [0.1.1] - 2019-05-25 -* Fix disconnect handling #834 +- Fix disconnect handling #834 ## [0.1.0] - 2019-05-18 -* Release +- Release ## [0.1.0-beta.4] - 2019-05-12 -* Handle cancellation of uploads #736 +- Handle cancellation of uploads #736 -* Upgrade to actix-web 1.0.0-beta.4 +- Upgrade to actix-web 1.0.0-beta.4 ## [0.1.0-beta.1] - 2019-04-21 -* Do not support nested multipart +- Do not support nested multipart -* Split multipart support to separate crate +- Split multipart support to separate crate -* Optimize multipart handling #634, #769 +- Optimize multipart handling #634, #769 diff --git a/actix-router/CHANGES.md b/actix-router/CHANGES.md index d0ed55c88..0a6a56359 100644 --- a/actix-router/CHANGES.md +++ b/actix-router/CHANGES.md @@ -4,20 +4,20 @@ ## 0.5.0-beta.3 - 2021-12-17 -* Minimum supported Rust version (MSRV) is now 1.52. +- Minimum supported Rust version (MSRV) is now 1.52. ## 0.5.0-beta.2 - 2021-09-09 -* Introduce `ResourceDef::join`. [#380] -* Disallow prefix routes with tail segments. [#379] -* Enforce path separators on dynamic prefixes. [#378] -* Improve malformed path error message. [#384] -* Prefix segments now always end with with a segment delimiter or end-of-input. [#2355] -* Prefix segments with trailing slashes define a trailing empty segment. [#2355] -* Support multi-pattern prefixes and joins. [#2356] -* `ResourceDef::pattern` now returns the first pattern in multi-pattern resources. [#2356] -* Support `build_resource_path` on multi-pattern resources. [#2356] -* Minimum supported Rust version (MSRV) is now 1.51. +- Introduce `ResourceDef::join`. [#380] +- Disallow prefix routes with tail segments. [#379] +- Enforce path separators on dynamic prefixes. [#378] +- Improve malformed path error message. [#384] +- Prefix segments now always end with with a segment delimiter or end-of-input. [#2355] +- Prefix segments with trailing slashes define a trailing empty segment. [#2355] +- Support multi-pattern prefixes and joins. [#2356] +- `ResourceDef::pattern` now returns the first pattern in multi-pattern resources. [#2356] +- Support `build_resource_path` on multi-pattern resources. [#2356] +- Minimum supported Rust version (MSRV) is now 1.51. [#378]: https://github.com/actix/actix-net/pull/378 [#379]: https://github.com/actix/actix-net/pull/379 @@ -28,23 +28,23 @@ ## 0.5.0-beta.1 - 2021-07-20 -* Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366] -* Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373] -* Fix segment interpolation leaving `Path` in unintended state after matching. [#368] -* Fix `ResourceDef` `PartialEq` implementation. [#373] -* Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372] -* Implement `IntoPatterns` for `bytestring::ByteString`. [#372] -* Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370] -* Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371] -* `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373] -* Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371] -* Rename `ResourceDef::{is_prefix_match => find_match}`. [#373] -* Rename `ResourceDef::{match_path => capture_match_info}`. [#373] -* Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373] -* Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373] -* Rename `Router::{*_checked => *_fn}`. [#373] -* Return type of `ResourceDef::name` is now `Option<&str>`. [#373] -* Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373] +- Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366] +- Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373] +- Fix segment interpolation leaving `Path` in unintended state after matching. [#368] +- Fix `ResourceDef` `PartialEq` implementation. [#373] +- Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372] +- Implement `IntoPatterns` for `bytestring::ByteString`. [#372] +- Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370] +- Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371] +- `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373] +- Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371] +- Rename `ResourceDef::{is_prefix_match => find_match}`. [#373] +- Rename `ResourceDef::{match_path => capture_match_info}`. [#373] +- Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373] +- Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373] +- Rename `Router::{*_checked => *_fn}`. [#373] +- Return type of `ResourceDef::name` is now `Option<&str>`. [#373] +- Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373] [#368]: https://github.com/actix/actix-net/pull/368 [#366]: https://github.com/actix/actix-net/pull/366 @@ -56,10 +56,10 @@ ## 0.4.0 - 2021-06-06 -* When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357] -* Path tail patterns now match new lines (`\n`) in request URL. [#360] -* Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359] -* Methods `Path::{add, add_static}` now take `impl Into>`. [#345] +- When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357] +- Path tail patterns now match new lines (`\n`) in request URL. [#360] +- Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359] +- Methods `Path::{add, add_static}` now take `impl Into>`. [#345] [#345]: https://github.com/actix/actix-net/pull/345 [#357]: https://github.com/actix/actix-net/pull/357 @@ -68,68 +68,68 @@ ## 0.3.0 - 2019-12-31 -* Version was yanked previously. See https://crates.io/crates/actix-router/0.3.0 +- Version was yanked previously. See https://crates.io/crates/actix-router/0.3.0 ## 0.2.7 - 2021-02-06 -* Add `Router::recognize_checked` [#247] +- Add `Router::recognize_checked` [#247] [#247]: https://github.com/actix/actix-net/pull/247 ## 0.2.6 - 2021-01-09 -* Use `bytestring` version range compatible with Bytes v1.0. [#246] +- Use `bytestring` version range compatible with Bytes v1.0. [#246] [#246]: https://github.com/actix/actix-net/pull/246 ## 0.2.5 - 2020-09-20 -* Fix `from_hex()` method +- Fix `from_hex()` method ## 0.2.4 - 2019-12-31 -* Add `ResourceDef::resource_path_named()` path generation method +- Add `ResourceDef::resource_path_named()` path generation method ## 0.2.3 - 2019-12-25 -* Add impl `IntoPattern` for `&String` +- Add impl `IntoPattern` for `&String` ## 0.2.2 - 2019-12-25 -* Use `IntoPattern` for `RouterBuilder::path()` +- Use `IntoPattern` for `RouterBuilder::path()` ## 0.2.1 - 2019-12-25 -* Add `IntoPattern` trait -* Add multi-pattern resources +- Add `IntoPattern` trait +- Add multi-pattern resources ## 0.2.0 - 2019-12-07 -* Update http to 0.2 -* Update regex to 1.3 -* Use bytestring instead of string +- Update http to 0.2 +- Update regex to 1.3 +- Use bytestring instead of string ## 0.1.5 - 2019-05-15 -* Remove debug prints +- Remove debug prints ## 0.1.4 - 2019-05-15 -* Fix checked resource match +- Fix checked resource match ## 0.1.3 - 2019-04-22 -* Added support for `remainder match` (i.e "/path/{tail}*") +- Added support for `remainder match` (i.e "/path/{tail}*") ## 0.1.2 - 2019-04-07 -* Export `Quoter` type -* Allow to reset `Path` instance +- Export `Quoter` type +- Allow to reset `Path` instance ## 0.1.1 - 2019-04-03 -* Get dynamic segment by name instead of iterator. +- Get dynamic segment by name instead of iterator. ## 0.1.0 - 2019-03-09 -* Initial release +- Initial release diff --git a/actix-test/CHANGES.md b/actix-test/CHANGES.md index ef78ac54a..e3deeb3f4 100644 --- a/actix-test/CHANGES.md +++ b/actix-test/CHANGES.md @@ -4,46 +4,46 @@ ## 0.1.0-beta.9 - 2021-12-17 -* Re-export `actix_http::body::to_bytes`. [#2518] -* Update `actix_web::test` re-exports. [#2518] +- Re-export `actix_http::body::to_bytes`. [#2518] +- Update `actix_web::test` re-exports. [#2518] [#2518]: https://github.com/actix/actix-web/pull/2518 ## 0.1.0-beta.8 - 2021-12-11 -* No significant changes since `0.1.0-beta.7`. +- No significant changes since `0.1.0-beta.7`. ## 0.1.0-beta.7 - 2021-11-22 -* Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408] +- Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408] [#2408]: https://github.com/actix/actix-web/pull/2408 ## 0.1.0-beta.6 - 2021-11-15 -* No significant changes from `0.1.0-beta.5`. +- No significant changes from `0.1.0-beta.5`. ## 0.1.0-beta.5 - 2021-10-20 -* Updated rustls to v0.20. [#2414] -* Minimum supported Rust version (MSRV) is now 1.52. +- Updated rustls to v0.20. [#2414] +- Minimum supported Rust version (MSRV) is now 1.52. [#2414]: https://github.com/actix/actix-web/pull/2414 ## 0.1.0-beta.4 - 2021-09-09 -* Minimum supported Rust version (MSRV) is now 1.51. +- Minimum supported Rust version (MSRV) is now 1.51. ## 0.1.0-beta.3 - 2021-06-20 -* No significant changes from `0.1.0-beta.2`. +- No significant changes from `0.1.0-beta.2`. ## 0.1.0-beta.2 - 2021-04-17 -* No significant changes from `0.1.0-beta.1`. +- No significant changes from `0.1.0-beta.1`. ## 0.1.0-beta.1 - 2021-04-02 -* Move integration testing structs from `actix-web`. [#2112] +- Move integration testing structs from `actix-web`. [#2112] [#2112]: https://github.com/actix/actix-web/pull/2112 diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md index d3078499c..6abfe2c61 100644 --- a/actix-web-actors/CHANGES.md +++ b/actix-web-actors/CHANGES.md @@ -4,105 +4,105 @@ ## 4.0.0-beta.8 - 2021-12-11 -* Add `ws:WsResponseBuilder` for building WebSocket session response. [#1920] -* Deprecate `ws::{start_with_addr, start_with_protocols}`. [#1920] -* Minimum supported Rust version (MSRV) is now 1.52. +- Add `ws:WsResponseBuilder` for building WebSocket session response. [#1920] +- Deprecate `ws::{start_with_addr, start_with_protocols}`. [#1920] +- Minimum supported Rust version (MSRV) is now 1.52. [#1920]: https://github.com/actix/actix-web/pull/1920 ## 4.0.0-beta.7 - 2021-09-09 -* Minimum supported Rust version (MSRV) is now 1.51. +- Minimum supported Rust version (MSRV) is now 1.51. ## 4.0.0-beta.6 - 2021-06-26 -* Update `actix` to `0.12`. [#2277] +- Update `actix` to `0.12`. [#2277] [#2277]: https://github.com/actix/actix-web/pull/2277 ## 4.0.0-beta.5 - 2021-06-17 -* No notable changes. +- No notable changes. ## 4.0.0-beta.4 - 2021-04-02 -* No notable changes. +- No notable changes. ## 4.0.0-beta.3 - 2021-03-09 -* No notable changes. +- No notable changes. ## 4.0.0-beta.2 - 2021-02-10 -* No notable changes. +- No notable changes. ## 4.0.0-beta.1 - 2021-01-07 -* Update `pin-project` to `1.0`. -* Update `bytes` to `1.0`. [#1813] -* `WebsocketContext::text` now takes an `Into`. [#1864] +- Update `pin-project` to `1.0`. +- Update `bytes` to `1.0`. [#1813] +- `WebsocketContext::text` now takes an `Into`. [#1864] [#1813]: https://github.com/actix/actix-web/pull/1813 [#1864]: https://github.com/actix/actix-web/pull/1864 ## 3.0.0 - 2020-09-11 -* No significant changes from `3.0.0-beta.2`. +- No significant changes from `3.0.0-beta.2`. ## 3.0.0-beta.2 - 2020-09-10 -* Update `actix-*` dependencies to latest versions. +- Update `actix-*` dependencies to latest versions. ## [3.0.0-beta.1] - 2020-xx-xx -* Update `actix-web` & `actix-http` dependencies to beta.1 -* Bump minimum supported Rust version to 1.40 +- Update `actix-web` & `actix-http` dependencies to beta.1 +- Bump minimum supported Rust version to 1.40 ## [3.0.0-alpha.1] - 2020-05-08 -* Update the actix-web dependency to 3.0.0-alpha.1 -* Update the actix dependency to 0.10.0-alpha.2 -* Update the actix-http dependency to 2.0.0-alpha.3 +- Update the actix-web dependency to 3.0.0-alpha.1 +- Update the actix dependency to 0.10.0-alpha.2 +- Update the actix-http dependency to 2.0.0-alpha.3 ## [2.0.0] - 2019-12-20 -* Release +- Release ## [2.0.0-alpha.1] - 2019-12-15 -* Migrate to actix-web 2.0.0 +- Migrate to actix-web 2.0.0 ## [1.0.4] - 2019-12-07 -* Allow comma-separated websocket subprotocols without spaces (#1172) +- Allow comma-separated websocket subprotocols without spaces (#1172) ## [1.0.3] - 2019-11-14 -* Update actix-web and actix-http dependencies +- Update actix-web and actix-http dependencies ## [1.0.2] - 2019-07-20 -* Add `ws::start_with_addr()`, returning the address of the created actor, along +- Add `ws::start_with_addr()`, returning the address of the created actor, along with the `HttpResponse`. -* Add support for specifying protocols on websocket handshake #835 +- Add support for specifying protocols on websocket handshake #835 ## [1.0.1] - 2019-06-28 -* Allow to use custom ws codec with `WebsocketContext` #925 +- Allow to use custom ws codec with `WebsocketContext` #925 ## [1.0.0] - 2019-05-29 -* Update actix-http and actix-web +- Update actix-http and actix-web ## [0.1.0-alpha.3] - 2019-04-02 -* Update actix-http and actix-web +- Update actix-http and actix-web ## [0.1.0-alpha.2] - 2019-03-29 -* Update actix-http and actix-web +- Update actix-http and actix-web ## [0.1.0-alpha.1] - 2019-03-28 -* Initial impl +- Initial impl diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index 309274563..0d881d303 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -4,101 +4,101 @@ ## 0.5.0-beta.6 - 2021-12-11 -* No significant changes since `0.5.0-beta.5`. +- No significant changes since `0.5.0-beta.5`. ## 0.5.0-beta.5 - 2021-10-20 -* Improve error recovery potential when macro input is invalid. [#2410] -* Add `#[actix_web::test]` macro for setting up tests with a runtime. [#2409] -* Minimum supported Rust version (MSRV) is now 1.52. +- Improve error recovery potential when macro input is invalid. [#2410] +- Add `#[actix_web::test]` macro for setting up tests with a runtime. [#2409] +- Minimum supported Rust version (MSRV) is now 1.52. [#2410]: https://github.com/actix/actix-web/pull/2410 [#2409]: https://github.com/actix/actix-web/pull/2409 ## 0.5.0-beta.4 - 2021-09-09 -* In routing macros, paths are now validated at compile time. [#2350] -* Minimum supported Rust version (MSRV) is now 1.51. +- In routing macros, paths are now validated at compile time. [#2350] +- Minimum supported Rust version (MSRV) is now 1.51. [#2350]: https://github.com/actix/actix-web/pull/2350 ## 0.5.0-beta.3 - 2021-06-17 -* No notable changes. +- No notable changes. ## 0.5.0-beta.2 - 2021-03-09 -* Preserve doc comments when using route macros. [#2022] -* Add `name` attribute to `route` macro. [#1934] +- Preserve doc comments when using route macros. [#2022] +- Add `name` attribute to `route` macro. [#1934] [#2022]: https://github.com/actix/actix-web/pull/2022 [#1934]: https://github.com/actix/actix-web/pull/1934 ## 0.5.0-beta.1 - 2021-02-10 -* Use new call signature for `System::new`. +- Use new call signature for `System::new`. ## 0.4.0 - 2020-09-20 -* Added compile success and failure testing. [#1677] -* Add `route` macro for supporting multiple HTTP methods guards. [#1674] +- Added compile success and failure testing. [#1677] +- Add `route` macro for supporting multiple HTTP methods guards. [#1674] [#1677]: https://github.com/actix/actix-web/pull/1677 [#1674]: https://github.com/actix/actix-web/pull/1674 ## 0.3.0 - 2020-09-11 -* No significant changes from `0.3.0-beta.1`. +- No significant changes from `0.3.0-beta.1`. ## 0.3.0-beta.1 - 2020-07-14 -* Add main entry-point macro that uses re-exported runtime. [#1559] +- Add main entry-point macro that uses re-exported runtime. [#1559] [#1559]: https://github.com/actix/actix-web/pull/1559 ## 0.2.2 - 2020-05-23 -* Add resource middleware on actix-web-codegen [#1467] +- Add resource middleware on actix-web-codegen [#1467] [#1467]: https://github.com/actix/actix-web/pull/1467 ## 0.2.1 - 2020-02-25 -* Add `#[allow(missing_docs)]` attribute to generated structs [#1368] -* Allow the handler function to be named as `config` [#1290] +- Add `#[allow(missing_docs)]` attribute to generated structs [#1368] +- Allow the handler function to be named as `config` [#1290] [#1368]: https://github.com/actix/actix-web/issues/1368 [#1290]: https://github.com/actix/actix-web/issues/1290 ## 0.2.0 - 2019-12-13 -* Generate code for actix-web 2.0 +- Generate code for actix-web 2.0 ## 0.1.3 - 2019-10-14 -* Bump up `syn` & `quote` to 1.0 -* Provide better error message +- Bump up `syn` & `quote` to 1.0 +- Provide better error message ## 0.1.2 - 2019-06-04 -* Add macros for head, options, trace, connect and patch http methods +- Add macros for head, options, trace, connect and patch http methods ## 0.1.1 - 2019-06-01 -* Add syn "extra-traits" feature +- Add syn "extra-traits" feature ## 0.1.0 - 2019-05-18 -* Release +- Release ## 0.1.0-beta.1 - 2019-04-20 -* Gen code for actix-web 1.0.0-beta.1 +- Gen code for actix-web 1.0.0-beta.1 ## 0.1.0-alpha.6 - 2019-04-14 -* Gen code for actix-web 1.0.0-alpha.6 +- Gen code for actix-web 1.0.0-alpha.6 ## 0.1.0-alpha.1 - 2019-03-28 -* Initial impl +- Initial impl diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 7b822930c..b5144b7a2 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,75 +1,75 @@ # Changes ## Unreleased - 2021-xx-xx -* Rename `Connector::{ssl => openssl}`. [#2503] -* Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503] +- Rename `Connector::{ssl => openssl}`. [#2503] +- Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503] [#2503]: https://github.com/actix/actix-web/pull/2503 ## 3.0.0-beta.14 - 2021-12-17 -* Add `ClientBuilder::add_default_header` and deprecate `ClientBuilder::header`. [#2510] +- Add `ClientBuilder::add_default_header` and deprecate `ClientBuilder::header`. [#2510] [#2510]: https://github.com/actix/actix-web/pull/2510 ## 3.0.0-beta.13 - 2021-12-11 -* No significant changes since `3.0.0-beta.12`. +- No significant changes since `3.0.0-beta.12`. ## 3.0.0-beta.12 - 2021-11-30 -* Update `actix-tls` to `3.0.0-rc.1`. [#2474] +- Update `actix-tls` to `3.0.0-rc.1`. [#2474] [#2474]: https://github.com/actix/actix-web/pull/2474 ## 3.0.0-beta.11 - 2021-11-22 -* No significant changes from `3.0.0-beta.10`. +- No significant changes from `3.0.0-beta.10`. ## 3.0.0-beta.10 - 2021-11-15 -* No significant changes from `3.0.0-beta.9`. +- No significant changes from `3.0.0-beta.9`. ## 3.0.0-beta.9 - 2021-10-20 -* Updated rustls to v0.20. [#2414] +- Updated rustls to v0.20. [#2414] [#2414]: https://github.com/actix/actix-web/pull/2414 ## 3.0.0-beta.8 - 2021-09-09 ### Changed -* Send headers within the redirect requests. [#2310] +- Send headers within the redirect requests. [#2310] [#2310]: https://github.com/actix/actix-web/pull/2310 ## 3.0.0-beta.7 - 2021-06-26 ### Changed -* Change compression algorithm features flags. [#2250] +- Change compression algorithm features flags. [#2250] [#2250]: https://github.com/actix/actix-web/pull/2250 ## 3.0.0-beta.6 - 2021-06-17 -* No significant changes since 3.0.0-beta.5. +- No significant changes since 3.0.0-beta.5. ## 3.0.0-beta.5 - 2021-04-17 ### Removed -* Deprecated methods on `ClientRequest`: `if_true`, `if_some`. [#2148] +- Deprecated methods on `ClientRequest`: `if_true`, `if_some`. [#2148] [#2148]: https://github.com/actix/actix-web/pull/2148 ## 3.0.0-beta.4 - 2021-04-02 ### Added -* Add `Client::headers` to get default mut reference of `HeaderMap` of client object. [#2114] +- Add `Client::headers` to get default mut reference of `HeaderMap` of client object. [#2114] ### Changed -* `ConnectorService` type is renamed to `BoxConnectorService`. [#2081] -* Fix http/https encoding when enabling `compress` feature. [#2116] -* Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header +- `ConnectorService` type is renamed to `BoxConnectorService`. [#2081] +- Fix http/https encoding when enabling `compress` feature. [#2116] +- Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header methods now take `TryIntoHeaderPair` tuples. [#2094] [#2081]: https://github.com/actix/actix-web/pull/2081 @@ -80,16 +80,16 @@ ## 3.0.0-beta.3 - 2021-03-08 ### Added -* `ClientResponse::timeout` for set the timeout of collecting response body. [#1931] -* `ClientBuilder::local_address` for bind to a local ip address for this client. [#2024] +- `ClientResponse::timeout` for set the timeout of collecting response body. [#1931] +- `ClientBuilder::local_address` for bind to a local ip address for this client. [#2024] ### Changed -* Feature `cookies` is now optional and enabled by default. [#1981] -* `ClientBuilder::connector` method would take `actix_http::client::Connector` type. [#2008] -* Basic auth password now takes blank passwords as an empty string instead of Option. [#2050] +- Feature `cookies` is now optional and enabled by default. [#1981] +- `ClientBuilder::connector` method would take `actix_http::client::Connector` type. [#2008] +- Basic auth password now takes blank passwords as an empty string instead of Option. [#2050] ### Removed -* `ClientBuilder::default` function [#2008] +- `ClientBuilder::default` function [#2008] [#1931]: https://github.com/actix/actix-web/pull/1931 [#1981]: https://github.com/actix/actix-web/pull/1981 @@ -100,18 +100,18 @@ ## 3.0.0-beta.2 - 2021-02-10 ### Added -* `ClientRequest::insert_header` method which allows using typed headers. [#1869] -* `ClientRequest::append_header` method which allows using typed headers. [#1869] -* `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969] +- `ClientRequest::insert_header` method which allows using typed headers. [#1869] +- `ClientRequest::append_header` method which allows using typed headers. [#1869] +- `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969] ### Changed -* Relax default timeout for `Connector` to 5 seconds(original 1 second). [#1905] +- Relax default timeout for `Connector` to 5 seconds(original 1 second). [#1905] ### Removed -* `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869] -* `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869] -* `ClientRequest::set_header_if_none`; use `ClientRequest::insert_header_if_none`. [#1869] -* `ClientRequest::header`; use `ClientRequest::append_header`. [#1869] +- `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869] +- `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869] +- `ClientRequest::set_header_if_none`; use `ClientRequest::insert_header_if_none`. [#1869] +- `ClientRequest::header`; use `ClientRequest::append_header`. [#1869] [#1869]: https://github.com/actix/actix-web/pull/1869 [#1905]: https://github.com/actix/actix-web/pull/1905 @@ -120,32 +120,32 @@ ## 3.0.0-beta.1 - 2021-01-07 ### Changed -* Update `rand` to `0.8` -* Update `bytes` to `1.0`. [#1813] -* Update `rust-tls` to `0.19`. [#1813] +- Update `rand` to `0.8` +- Update `bytes` to `1.0`. [#1813] +- Update `rust-tls` to `0.19`. [#1813] [#1813]: https://github.com/actix/actix-web/pull/1813 ## 2.0.3 - 2020-11-29 ### Fixed -* Ensure `actix-http` dependency uses same `serde_urlencoded`. +- Ensure `actix-http` dependency uses same `serde_urlencoded`. ## 2.0.2 - 2020-11-25 ### Changed -* Upgrade `serde_urlencoded` to `0.7`. [#1773] +- Upgrade `serde_urlencoded` to `0.7`. [#1773] [#1773]: https://github.com/actix/actix-web/pull/1773 ## 2.0.1 - 2020-10-30 ### Changed -* Upgrade `base64` to `0.13`. [#1744] -* Deprecate `ClientRequest::{if_some, if_true}`. [#1760] +- Upgrade `base64` to `0.13`. [#1744] +- Deprecate `ClientRequest::{if_some, if_true}`. [#1760] ### Fixed -* Use `Accept-Encoding: identity` instead of `Accept-Encoding: br` when no compression feature +- Use `Accept-Encoding: identity` instead of `Accept-Encoding: br` when no compression feature is enabled [#1737] [#1737]: https://github.com/actix/actix-web/pull/1737 @@ -155,209 +155,209 @@ ## 2.0.0 - 2020-09-11 ### Changed -* `Client::build` was renamed to `Client::builder`. +- `Client::build` was renamed to `Client::builder`. ## 2.0.0-beta.4 - 2020-09-09 ### Changed -* Update actix-codec & actix-tls dependencies. +- Update actix-codec & actix-tls dependencies. ## 2.0.0-beta.3 - 2020-08-17 ### Changed -* Update `rustls` to 0.18 +- Update `rustls` to 0.18 ## 2.0.0-beta.2 - 2020-07-21 ### Changed -* Update `actix-http` dependency to 2.0.0-beta.2 +- Update `actix-http` dependency to 2.0.0-beta.2 ## [2.0.0-beta.1] - 2020-07-14 ### Changed -* Update `actix-http` dependency to 2.0.0-beta.1 +- Update `actix-http` dependency to 2.0.0-beta.1 ## [2.0.0-alpha.2] - 2020-05-21 ### Changed -* Implement `std::error::Error` for our custom errors [#1422] -* Bump minimum supported Rust version to 1.40 -* Update `base64` dependency to 0.12 +- Implement `std::error::Error` for our custom errors [#1422] +- Bump minimum supported Rust version to 1.40 +- Update `base64` dependency to 0.12 [#1422]: https://github.com/actix/actix-web/pull/1422 ## [2.0.0-alpha.1] - 2020-03-11 -* Update `actix-http` dependency to 2.0.0-alpha.2 -* Update `rustls` dependency to 0.17 -* ClientBuilder accepts initial_window_size and initial_connection_window_size HTTP2 configuration -* ClientBuilder allowing to set max_http_version to limit HTTP version to be used +- Update `actix-http` dependency to 2.0.0-alpha.2 +- Update `rustls` dependency to 0.17 +- ClientBuilder accepts initial_window_size and initial_connection_window_size HTTP2 configuration +- ClientBuilder allowing to set max_http_version to limit HTTP version to be used ## [1.0.1] - 2019-12-15 -* Fix compilation with default features off +- Fix compilation with default features off ## [1.0.0] - 2019-12-13 -* Release +- Release ## [1.0.0-alpha.3] -* Migrate to `std::future` +- Migrate to `std::future` ## [0.2.8] - 2019-11-06 -* Add support for setting query from Serialize type for client request. +- Add support for setting query from Serialize type for client request. ## [0.2.7] - 2019-09-25 ### Added -* Remaining getter methods for `ClientRequest`'s private `head` field #1101 +- Remaining getter methods for `ClientRequest`'s private `head` field #1101 ## [0.2.6] - 2019-09-12 ### Added -* Export frozen request related types. +- Export frozen request related types. ## [0.2.5] - 2019-09-11 ### Added -* Add `FrozenClientRequest` to support retries for sending HTTP requests +- Add `FrozenClientRequest` to support retries for sending HTTP requests ### Changed -* Ensure that the `Host` header is set when initiating a WebSocket client connection. +- Ensure that the `Host` header is set when initiating a WebSocket client connection. ## [0.2.4] - 2019-08-13 ### Changed -* Update percent-encoding to "2.1" +- Update percent-encoding to "2.1" -* Update serde_urlencoded to "0.6.1" +- Update serde_urlencoded to "0.6.1" ## [0.2.3] - 2019-08-01 ### Added -* Add `rustls` support +- Add `rustls` support ## [0.2.2] - 2019-07-01 ### Changed -* Always append a colon after username in basic auth +- Always append a colon after username in basic auth -* Upgrade `rand` dependency version to 0.7 +- Upgrade `rand` dependency version to 0.7 ## [0.2.1] - 2019-06-05 ### Added -* Add license files +- Add license files ## [0.2.0] - 2019-05-12 ### Added -* Allow to send headers in `Camel-Case` form. +- Allow to send headers in `Camel-Case` form. ### Changed -* Upgrade actix-http dependency. +- Upgrade actix-http dependency. ## [0.1.1] - 2019-04-19 ### Added -* Allow to specify server address for http and ws requests. +- Allow to specify server address for http and ws requests. ### Changed -* `ClientRequest::if_true()` and `ClientRequest::if_some()` use instance instead of ref +- `ClientRequest::if_true()` and `ClientRequest::if_some()` use instance instead of ref ## [0.1.0] - 2019-04-16 -* No changes +- No changes ## [0.1.0-alpha.6] - 2019-04-14 ### Changed -* Do not set default headers for websocket request +- Do not set default headers for websocket request ## [0.1.0-alpha.5] - 2019-04-12 ### Changed -* Do not set any default headers +- Do not set any default headers ### Added -* Add Debug impl for BoxedSocket +- Add Debug impl for BoxedSocket ## [0.1.0-alpha.4] - 2019-04-08 ### Changed -* Update actix-http dependency +- Update actix-http dependency ## [0.1.0-alpha.3] - 2019-04-02 ### Added -* Export `MessageBody` type +- Export `MessageBody` type -* `ClientResponse::json()` - Loads and parse `application/json` encoded body +- `ClientResponse::json()` - Loads and parse `application/json` encoded body ### Changed -* `ClientRequest::json()` accepts reference instead of object. +- `ClientRequest::json()` accepts reference instead of object. -* `ClientResponse::body()` does not consume response object. +- `ClientResponse::body()` does not consume response object. -* Renamed `ClientRequest::close_connection()` to `ClientRequest::force_close()` +- Renamed `ClientRequest::close_connection()` to `ClientRequest::force_close()` ## [0.1.0-alpha.2] - 2019-03-29 ### Added -* Per request and session wide request timeout. +- Per request and session wide request timeout. -* Session wide headers. +- Session wide headers. -* Session wide basic and bearer auth. +- Session wide basic and bearer auth. -* Re-export `actix_http::client::Connector`. +- Re-export `actix_http::client::Connector`. ### Changed -* Allow to override request's uri +- Allow to override request's uri -* Export `ws` sub-module with websockets related types +- Export `ws` sub-module with websockets related types ## [0.1.0-alpha.1] - 2019-03-28 -* Initial impl +- Initial impl From 212c6926f97f439c2fbbe41f6bd10211653540e2 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 22 Dec 2021 08:18:44 +0000 Subject: [PATCH 61/87] Revert "use dash hyphenation in changelogs" This reverts commit 1ea619f2a1722206cddf4af0a43715fc8202a06e. --- CHANGES.md | 551 +++++++++++++++++------------------ actix-files/CHANGES.md | 102 +++---- actix-http-test/CHANGES.md | 76 ++--- actix-http/CHANGES.md | 544 +++++++++++++++++----------------- actix-multipart/CHANGES.md | 74 ++--- actix-router/CHANGES.md | 102 +++---- actix-test/CHANGES.md | 22 +- actix-web-actors/CHANGES.md | 60 ++-- actix-web-codegen/CHANGES.md | 52 ++-- awc/CHANGES.md | 170 +++++------ 10 files changed, 876 insertions(+), 877 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0458958c5..77ab2e218 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,29 +2,29 @@ ## Unreleased - 2021-xx-xx -- + ## 4.0.0-beta.15 - 2021-12-17 ### Added * Method on `Responder` trait (`customize`) for customizing responders and `CustomizeResponder` struct. [#2510] * Implement `Debug` for `DefaultHeaders`. [#2510] ### Changed -- Align `DefaultHeader` method terminology, deprecating previous methods. [#2510] -- Response service types in `ErrorHandlers` middleware now use `ServiceResponse>` to allow changing the body type. [#2515] +* Align `DefaultHeader` method terminology, deprecating previous methods. [#2510] +* Response service types in `ErrorHandlers` middleware now use `ServiceResponse>` to allow changing the body type. [#2515] * Both variants in `ErrorHandlerResponse` now use `ServiceResponse>`. [#2515] * Rename `test::{default_service => simple_service}`. Old name is deprecated. [#2518] -- Rename `test::{read_response_json => call_and_read_body_json}`. Old name is deprecated. [#2518] -- Rename `test::{read_response => call_and_read_body}`. Old name is deprecated. [#2518] -- Relax body type and error bounds on test utilities. [#2518] -- -- # Removed -- Top-level `EitherExtractError` export. [#2510] -- Conversion implementations for `either` crate. [#2516] +* Rename `test::{read_response_json => call_and_read_body_json}`. Old name is deprecated. [#2518] +* Rename `test::{read_response => call_and_read_body}`. Old name is deprecated. [#2518] +* Relax body type and error bounds on test utilities. [#2518] + +### Removed +* Top-level `EitherExtractError` export. [#2510] +* Conversion implementations for `either` crate. [#2516] * `test::load_stream` and `test::load_body`; replace usage with `body::to_bytes`. [#2518] -- 2510]: https://github.com/actix/actix-web/pull/2510 -- 2515]: https://github.com/actix/actix-web/pull/2515 -- 2516]: https://github.com/actix/actix-web/pull/2516 +[#2510]: https://github.com/actix/actix-web/pull/2510 +[#2515]: https://github.com/actix/actix-web/pull/2515 +[#2516]: https://github.com/actix/actix-web/pull/2516 [#2518]: https://github.com/actix/actix-web/pull/2518 @@ -34,31 +34,31 @@ * `AcceptEncoding` typed header. [#2482] * `Range` typed header. [#2485] * `HttpResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] -- `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] -- Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491] -- `HttpRequest::{req_data,req_data_mut}`. [#2487] -- `ServiceResponse::into_parts`. [#2499] -- -- # Changed -- Rename `Accept::{mime_precedence => ranked}`. [#2480] -- Rename `Accept::{mime_preference => preference}`. [#2480] +* `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] +* Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491] +* `HttpRequest::{req_data,req_data_mut}`. [#2487] +* `ServiceResponse::into_parts`. [#2499] + +### Changed +* Rename `Accept::{mime_precedence => ranked}`. [#2480] +* Rename `Accept::{mime_preference => preference}`. [#2480] * Un-deprecate `App::data_factory`. [#2484] * `HttpRequest::url_for` no longer constructs URLs with query or fragment components. [#2430] -- Remove `B` (body) type parameter on `App`. [#2493] -- Add `B` (body) type parameter on `Scope`. [#2492] -- Request-local data container is no longer part of a `RequestHead`. Instead it is a distinct part of a `Request`. [#2487] -- -- # Fixed -- Accept wildcard `*` items in `AcceptLanguage`. [#2480] -- Re-exports `dev::{BodySize, MessageBody, SizedStream}`. They are exposed through the `body` module. [#2468] +* Remove `B` (body) type parameter on `App`. [#2493] +* Add `B` (body) type parameter on `Scope`. [#2492] +* Request-local data container is no longer part of a `RequestHead`. Instead it is a distinct part of a `Request`. [#2487] + +### Fixed +* Accept wildcard `*` items in `AcceptLanguage`. [#2480] +* Re-exports `dev::{BodySize, MessageBody, SizedStream}`. They are exposed through the `body` module. [#2468] * Typed headers containing lists that require one or more items now enforce this minimum. [#2482] -- # Removed -- `ConnectionInfo::get`. [#2487] -- +### Removed +* `ConnectionInfo::get`. [#2487] + [#2430]: https://github.com/actix/actix-web/pull/2430 [#2468]: https://github.com/actix/actix-web/pull/2468 -- 2480]: https://github.com/actix/actix-web/pull/2480 +[#2480]: https://github.com/actix/actix-web/pull/2480 [#2482]: https://github.com/actix/actix-web/pull/2482 [#2484]: https://github.com/actix/actix-web/pull/2484 [#2485]: https://github.com/actix/actix-web/pull/2485 @@ -75,20 +75,20 @@ [#2474]: https://github.com/actix/actix-web/pull/2474 -- + ## 4.0.0-beta.12 - 2021-11-22 ### Changed * Compress middleware's response type is now `AnyBody>`. [#2448] ### Fixed * Relax `Unpin` bound on `S` (stream) parameter of `HttpResponseBuilder::streaming`. [#2448] -- + ### Removed * `dev::ResponseBody` re-export; is function is replaced by the new `dev::AnyBody` enum. [#2446] -- + [#2446]: https://github.com/actix/actix-web/pull/2446 [#2448]: https://github.com/actix/actix-web/pull/2448 -- + ## 4.0.0-beta.11 - 2021-11-15 ### Added @@ -96,11 +96,11 @@ ### Changed * `ContentType::html` now produces `text/html; charset=utf-8` instead of `text/html`. [#2423] -- Update `actix-server` to `2.0.0-beta.9`. [#2442] +* Update `actix-server` to `2.0.0-beta.9`. [#2442] [#2423]: https://github.com/actix/actix-web/pull/2423 -- 2442]: https://github.com/actix/actix-web/pull/2442 -- +[#2442]: https://github.com/actix/actix-web/pull/2442 + ## 4.0.0-beta.10 - 2021-10-20 ### Added @@ -108,18 +108,18 @@ * `#[actix_web::test]` macro for setting up tests with a runtime. [#2409] ### Changed -- Associated type `FromRequest::Config` was removed. [#2233] -- Inner field made private on `web::Payload`. [#2384] +* Associated type `FromRequest::Config` was removed. [#2233] +* Inner field made private on `web::Payload`. [#2384] * `Data::into_inner` and `Data::get_ref` no longer requires `T: Sized`. [#2403] * Updated rustls to v0.20. [#2414] -- Minimum supported Rust version (MSRV) is now 1.52. -- -- # Removed -- Useless `ServiceResponse::checked_expr` method. [#2401] -- +* Minimum supported Rust version (MSRV) is now 1.52. + +### Removed +* Useless `ServiceResponse::checked_expr` method. [#2401] + [#2233]: https://github.com/actix/actix-web/pull/2233 [#2362]: https://github.com/actix/actix-web/pull/2362 -- 2384]: https://github.com/actix/actix-web/pull/2384 +[#2384]: https://github.com/actix/actix-web/pull/2384 [#2401]: https://github.com/actix/actix-web/pull/2401 [#2403]: https://github.com/actix/actix-web/pull/2403 [#2409]: https://github.com/actix/actix-web/pull/2409 @@ -132,17 +132,17 @@ ### Changed * Compress middleware will return 406 Not Acceptable when no content encoding is acceptable to the client. [#2344] -- Move `BaseHttpResponse` to `dev::Response`. [#2379] +* Move `BaseHttpResponse` to `dev::Response`. [#2379] * Enable `TestRequest::param` to accept more than just static strings. [#2172] * Minimum supported Rust version (MSRV) is now 1.51. -- -- # Fixed -- Fix quality parse error in Accept-Encoding header. [#2344] -- Re-export correct type at `web::HttpResponse`. [#2379] + +### Fixed +* Fix quality parse error in Accept-Encoding header. [#2344] +* Re-export correct type at `web::HttpResponse`. [#2379] [#2172]: https://github.com/actix/actix-web/pull/2172 -- 2325]: https://github.com/actix/actix-web/pull/2325 -- 2344]: https://github.com/actix/actix-web/pull/2344 +[#2325]: https://github.com/actix/actix-web/pull/2325 +[#2344]: https://github.com/actix/actix-web/pull/2344 [#2379]: https://github.com/actix/actix-web/pull/2379 @@ -152,18 +152,18 @@ * Add extractors for `Uri` and `Method`. [#2263] * Add extractors for `ConnectionInfo` and `PeerAddr`. [#2263] * Add `Route::service` for using hand-written services as handlers. [#2262] -- -- # Changed -- Change compression algorithm features flags. [#2250] -- Deprecate `App::data` and `App::data_factory`. [#2271] + +### Changed +* Change compression algorithm features flags. [#2250] +* Deprecate `App::data` and `App::data_factory`. [#2271] * Smarter extraction of `ConnectionInfo` parts. [#2282] -- # Fixed -- Scope and Resource middleware can access data items set on their own layer. [#2288] -- +### Fixed +* Scope and Resource middleware can access data items set on their own layer. [#2288] + [#2177]: https://github.com/actix/actix-web/pull/2177 [#2250]: https://github.com/actix/actix-web/pull/2250 -- 2271]: https://github.com/actix/actix-web/pull/2271 +[#2271]: https://github.com/actix/actix-web/pull/2271 [#2262]: https://github.com/actix/actix-web/pull/2262 [#2263]: https://github.com/actix/actix-web/pull/2263 [#2282]: https://github.com/actix/actix-web/pull/2282 @@ -176,23 +176,23 @@ ### Changed * Adjusted default JSON payload limit to 2MB (from 32kb) and included size and limits in the `JsonPayloadError::Overflow` error variant. [#2162] -- 2162]: (https://github.com/actix/actix-web/pull/2162) +[#2162]: (https://github.com/actix/actix-web/pull/2162) * `ServiceResponse::error_response` now uses body type of `Body`. [#2201] * `ServiceResponse::checked_expr` now returns a `Result`. [#2201] -- Update `language-tags` to `0.3`. +* Update `language-tags` to `0.3`. * `ServiceResponse::take_body`. [#2201] -- `ServiceResponse::map_body` closure receives and returns `B` instead of `ResponseBody` types. [#2201] -- All error trait bounds in server service builders have changed from `Into` to `Into>`. [#2253] -- All error trait bounds in message body and stream impls changed from `Into` to `Into>`. [#2253] -- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226] -- `middleware::normalize` now will not try to normalize URIs with no valid path [#2246] -- -- # Removed -- `HttpResponse::take_body` and old `HttpResponse::into_body` method that casted body type. [#2201] -- +* `ServiceResponse::map_body` closure receives and returns `B` instead of `ResponseBody` types. [#2201] +* All error trait bounds in server service builders have changed from `Into` to `Into>`. [#2253] +* All error trait bounds in message body and stream impls changed from `Into` to `Into>`. [#2253] +* `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226] +* `middleware::normalize` now will not try to normalize URIs with no valid path [#2246] + +### Removed +* `HttpResponse::take_body` and old `HttpResponse::into_body` method that casted body type. [#2201] + [#2200]: https://github.com/actix/actix-web/pull/2200 [#2201]: https://github.com/actix/actix-web/pull/2201 -- 2253]: https://github.com/actix/actix-web/pull/2253 +[#2253]: https://github.com/actix/actix-web/pull/2253 [#2246]: https://github.com/actix/actix-web/pull/2246 @@ -202,11 +202,11 @@ ### Changed * Most error types are now marked `#[non_exhaustive]`. [#2148] -- Methods on `ContentDisposition` that took `T: AsRef` now take `impl AsRef`. +* Methods on `ContentDisposition` that took `T: AsRef` now take `impl AsRef`. [#2065]: https://github.com/actix/actix-web/pull/2065 -- 2148]: https://github.com/actix/actix-web/pull/2148 -- +[#2148]: https://github.com/actix/actix-web/pull/2148 + ## 4.0.0-beta.5 - 2021-04-02 ### Added @@ -214,20 +214,20 @@ * Added `TestServer::client_headers` method. [#2097] ### Fixed -- Double ampersand in Logger format is escaped correctly. [#2067] -- +* Double ampersand in Logger format is escaped correctly. [#2067] + ### Changed * `CustomResponder` would return error as `HttpResponse` when `CustomResponder::with_header` failed -- instead of skipping. (Only the first error is kept when multiple error occur) [#2093] + instead of skipping. (Only the first error is kept when multiple error occur) [#2093] ### Removed -- The `client` mod was removed. Clients should now use `awc` directly. +* The `client` mod was removed. Clients should now use `awc` directly. [871ca5e4](https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7) * Integration testing was moved to new `actix-test` crate. Namely these items from the `test` module: `TestServer`, `TestServerConfig`, `start`, `start_with`, and `unused_addr`. [#2112] -- + [#2067]: https://github.com/actix/actix-web/pull/2067 -- 2093]: https://github.com/actix/actix-web/pull/2093 +[#2093]: https://github.com/actix/actix-web/pull/2093 [#2094]: https://github.com/actix/actix-web/pull/2094 [#2097]: https://github.com/actix/actix-web/pull/2097 [#2112]: https://github.com/actix/actix-web/pull/2112 @@ -239,8 +239,8 @@ * `JsonBody::new` returns a default limit of 32kB to be consistent with `JsonConfig` and the default behaviour of the `web::Json` extractor. [#2010] -- 1981]: https://github.com/actix/actix-web/pull/1981 -- 2010]: https://github.com/actix/actix-web/pull/2010 +[#1981]: https://github.com/actix/actix-web/pull/1981 +[#2010]: https://github.com/actix/actix-web/pull/2010 ## 4.0.0-beta.3 - 2021-02-10 @@ -248,36 +248,36 @@ ## 4.0.0-beta.2 - 2021-02-10 -- # Added +### Added * The method `Either, web::Form>::into_inner()` which returns the inner type for whichever variant was created. Also works for `Either, web::Json>`. [#1894] * Add `services!` macro for helping register multiple services to `App`. [#1933] * Enable registering a vec of services of the same type to `App` [#1933] -- + ### Changed -- Rework `Responder` trait to be sync and returns `Response`/`HttpResponse` directly. -- Making it simpler and more performant. [#1891] +* Rework `Responder` trait to be sync and returns `Response`/`HttpResponse` directly. + Making it simpler and more performant. [#1891] * `ServiceRequest::into_parts` and `ServiceRequest::from_parts` can no longer fail. [#1893] * `ServiceRequest::from_request` can no longer fail. [#1893] -- Our `Either` type now uses `Left`/`Right` variants (instead of `A`/`B`) [#1894] +* Our `Either` type now uses `Left`/`Right` variants (instead of `A`/`B`) [#1894] * `test::{call_service, read_response, read_response_json, send_request}` take `&Service` -- in argument [#1905] -- `App::wrap_fn`, `Resource::wrap_fn` and `Scope::wrap_fn` provide `&Service` in closure -- argument. [#1905] -- `web::block` no longer requires the output is a Result. [#1957] + in argument [#1905] +* `App::wrap_fn`, `Resource::wrap_fn` and `Scope::wrap_fn` provide `&Service` in closure + argument. [#1905] +* `web::block` no longer requires the output is a Result. [#1957] -- # Fixed +### Fixed * Multiple calls to `App::data` with the same type now keeps the latest call's data. [#1906] -- + ### Removed * Public field of `web::Path` has been made private. [#1894] -- Public field of `web::Query` has been made private. [#1894] +* Public field of `web::Query` has been made private. [#1894] * `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869] * `AppService::set_service_data`; for custom HTTP service factories adding application data, use the -- layered data model by calling `ServiceRequest::add_data_container` when handling -- requests instead. [#1906] -- -- 1891]: https://github.com/actix/actix-web/pull/1891 + layered data model by calling `ServiceRequest::add_data_container` when handling + requests instead. [#1906] + +[#1891]: https://github.com/actix/actix-web/pull/1891 [#1893]: https://github.com/actix/actix-web/pull/1893 [#1894]: https://github.com/actix/actix-web/pull/1894 [#1869]: https://github.com/actix/actix-web/pull/1869 @@ -293,26 +293,26 @@ `Compress` to be used in `middleware::Condition` and `Resource`, `Scope` services. [#1865] ### Changed -- Update `actix-*` dependencies to tokio `1.0` based versions. [#1813] +* Update `actix-*` dependencies to tokio `1.0` based versions. [#1813] * Bumped `rand` to `0.8`. * Update `rust-tls` to `0.19`. [#1813] * Rename `Handler` to `HandlerService` and rename `Factory` to `Handler`. [#1852] -- The default `TrailingSlash` is now `Trim`, in line with existing documentation. See migration -- guide for implications. [#1875] -- Rename `DefaultHeaders::{content_type => add_content_type}`. [#1875] -- MSRV is now 1.46.0. -- +* The default `TrailingSlash` is now `Trim`, in line with existing documentation. See migration + guide for implications. [#1875] +* Rename `DefaultHeaders::{content_type => add_content_type}`. [#1875] +* MSRV is now 1.46.0. + ### Fixed -- Added the underlying parse error to `test::read_body_json`'s panic message. [#1812] -- +* Added the underlying parse error to `test::read_body_json`'s panic message. [#1812] + ### Removed * Public modules `middleware::{normalize, err_handlers}`. All necessary middleware structs are now -- exposed directly by the `middleware` module. + exposed directly by the `middleware` module. * Remove `actix-threadpool` as dependency. `actix_threadpool::BlockingError` error type can be imported from `actix_web::error` module. [#1878] -- + [#1812]: https://github.com/actix/actix-web/pull/1812 -- 1813]: https://github.com/actix/actix-web/pull/1813 +[#1813]: https://github.com/actix/actix-web/pull/1813 [#1852]: https://github.com/actix/actix-web/pull/1852 [#1865]: https://github.com/actix/actix-web/pull/1865 [#1875]: https://github.com/actix/actix-web/pull/1875 @@ -325,16 +325,16 @@ [#2529]: https://github.com/actix/actix-web/pull/2529 -- + ## 3.3.2 - 2020-12-01 ### Fixed * Removed an occasional `unwrap` on `None` panic in `NormalizePathNormalization`. [#1762] * Fix `match_pattern()` returning `None` for scope with empty path resource. [#1798] * Increase minimum `socket2` version. [#1803] -- 1762]: https://github.com/actix/actix-web/pull/1762 -- 1798]: https://github.com/actix/actix-web/pull/1798 -- 1803]: https://github.com/actix/actix-web/pull/1803 +[#1762]: https://github.com/actix/actix-web/pull/1762 +[#1798]: https://github.com/actix/actix-web/pull/1798 +[#1803]: https://github.com/actix/actix-web/pull/1803 ## 3.3.1 - 2020-11-29 @@ -342,15 +342,15 @@ ## 3.3.0 - 2020-11-25 -- # Added +### Added * Add `Either` extractor helper. [#1788] ### Changed * Upgrade `serde_urlencoded` to `0.7`. [#1773] -- + [#1773]: https://github.com/actix/actix-web/pull/1773 [#1788]: https://github.com/actix/actix-web/pull/1788 -- + ## 3.2.0 - 2020-10-30 ### Added @@ -358,17 +358,17 @@ * Add request-local data extractor `web::ReqData`. [#1748] * Add ability to register closure for request middleware logging. [#1749] * Add `app_data` to `ServiceConfig`. [#1757] -- Expose `on_connect` for access to the connection stream before request is handled. [#1754] -- -- # Changed -- Updated actix-web-codegen dependency for access to new `#[route(...)]` multi-method macro. -- Print non-configured `Data` type when attempting extraction. [#1743] +* Expose `on_connect` for access to the connection stream before request is handled. [#1754] + +### Changed +* Updated actix-web-codegen dependency for access to new `#[route(...)]` multi-method macro. +* Print non-configured `Data` type when attempting extraction. [#1743] * Re-export bytes::Buf{Mut} in web module. [#1750] * Upgrade `pin-project` to `1.0`. -- -- 1723]: https://github.com/actix/actix-web/pull/1723 -- 1743]: https://github.com/actix/actix-web/pull/1743 -- 1748]: https://github.com/actix/actix-web/pull/1748 + +[#1723]: https://github.com/actix/actix-web/pull/1723 +[#1743]: https://github.com/actix/actix-web/pull/1743 +[#1748]: https://github.com/actix/actix-web/pull/1748 [#1750]: https://github.com/actix/actix-web/pull/1750 [#1754]: https://github.com/actix/actix-web/pull/1754 [#1749]: https://github.com/actix/actix-web/pull/1749 @@ -380,13 +380,13 @@ to retain any trailing slashes. [#1695] * Remove bound `std::marker::Sized` from `web::Data` to support storing `Arc` via `web::Data::from` [#1710] -- + ### Fixed -- `ResourceMap` debug printing is no longer infinitely recursive. [#1708] +* `ResourceMap` debug printing is no longer infinitely recursive. [#1708] [#1695]: https://github.com/actix/actix-web/pull/1695 [#1708]: https://github.com/actix/actix-web/pull/1708 -- 1710]: https://github.com/actix/actix-web/pull/1710 +[#1710]: https://github.com/actix/actix-web/pull/1710 ## 3.0.2 - 2020-09-15 @@ -395,33 +395,33 @@ [#1678]: https://github.com/actix/actix-web/pull/1678 -- + ## 3.0.1 - 2020-09-13 ### Changed * `middleware::normalize::TrailingSlash` enum is now accessible. [#1673] [#1673]: https://github.com/actix/actix-web/pull/1673 -- + ## 3.0.0 - 2020-09-11 * No significant changes from `3.0.0-beta.4`. ## 3.0.0-beta.4 - 2020-09-09 -- # Added +### Added * `middleware::NormalizePath` now has configurable behavior for either always having a trailing slash, or as the new addition, always trimming trailing slashes. [#1639] ### Changed -- Update actix-codec and actix-utils dependencies. [#1634] +* Update actix-codec and actix-utils dependencies. [#1634] * `FormConfig` and `JsonConfig` configurations are now also considered when set using `App::data`. [#1641] * `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`. [#1655] -- `HttpServer::maxconnrate` is renamed to the more expressive -- `HttpServer::max_connection_rate`. [#1655] +* `HttpServer::maxconnrate` is renamed to the more expressive + `HttpServer::max_connection_rate`. [#1655] -- 1639]: https://github.com/actix/actix-web/pull/1639 -- 1641]: https://github.com/actix/actix-web/pull/1641 +[#1639]: https://github.com/actix/actix-web/pull/1639 +[#1641]: https://github.com/actix/actix-web/pull/1641 [#1634]: https://github.com/actix/actix-web/pull/1634 [#1655]: https://github.com/actix/actix-web/pull/1655 @@ -431,22 +431,22 @@ ## 3.0.0-beta.2 - 2020-08-17 -- # Changed +### Changed * `PayloadConfig` is now also considered in `Bytes` and `String` extractors when set using `App::data`. [#1610] * `web::Path` now has a public representation: `web::Path(pub T)` that enables destructuring. [#1594] -- `ServiceRequest::app_data` allows retrieval of non-Data data without splitting into parts to +* `ServiceRequest::app_data` allows retrieval of non-Data data without splitting into parts to access `HttpRequest` which already allows this. [#1618] -- Re-export all error types from `awc`. [#1621] +* Re-export all error types from `awc`. [#1621] * MSRV is now 1.42.0. -- + ### Fixed -- Memory leak of app data in pooled requests. [#1609] -- +* Memory leak of app data in pooled requests. [#1609] + [#1594]: https://github.com/actix/actix-web/pull/1594 [#1609]: https://github.com/actix/actix-web/pull/1609 -- 1610]: https://github.com/actix/actix-web/pull/1610 +[#1610]: https://github.com/actix/actix-web/pull/1610 [#1618]: https://github.com/actix/actix-web/pull/1618 [#1621]: https://github.com/actix/actix-web/pull/1621 @@ -457,29 +457,29 @@ * `HttpRequest::match_pattern` and `ServiceRequest::match_pattern` for extracting the matched resource pattern. * `HttpRequest::match_name` and `ServiceRequest::match_name` for extracting matched resource name. -- -- # Changed + +### Changed * Fix actix_http::h1::dispatcher so it returns when HW_BUFFER_SIZE is reached. Should reduce peak memory consumption during large uploads. [#1550] -- Migrate cookie handling to `cookie` crate. Actix-web no longer requires `ring` dependency. +* Migrate cookie handling to `cookie` crate. Actix-web no longer requires `ring` dependency. * MSRV is now 1.41.1 -- # Fixed -- `NormalizePath` improved consistency when path needs slashes added _and_ removed. -- +### Fixed +* `NormalizePath` improved consistency when path needs slashes added _and_ removed. + ## 3.0.0-alpha.3 - 2020-05-21 -- # Added +### Added * Add option to create `Data` from `Arc` [#1509] ### Changed * Resources and Scopes can now access non-overridden data types set on App (or containing scopes) when setting their own data. [#1486] -- Fix audit issue logging by default peer address [#1485] +* Fix audit issue logging by default peer address [#1485] * Bump minimum supported Rust version to 1.40 * Replace deprecated `net2` crate with `socket2` -- -- 1485]: https://github.com/actix/actix-web/pull/1485 -- 1509]: https://github.com/actix/actix-web/pull/1509 -- + +[#1485]: https://github.com/actix/actix-web/pull/1485 +[#1509]: https://github.com/actix/actix-web/pull/1509 + ## [3.0.0-alpha.2] - 2020-05-08 ### Changed @@ -488,10 +488,10 @@ * Implement `std::error::Error` for our custom errors [#1422] * NormalizePath middleware now appends trailing / so that routes of form /example/ respond to /example requests. [#1433] * Remove the `failure` feature and support. -- -- 1422]: https://github.com/actix/actix-web/pull/1422 -- 1433]: https://github.com/actix/actix-web/pull/1433 -- 1452]: https://github.com/actix/actix-web/pull/1452 + +[#1422]: https://github.com/actix/actix-web/pull/1422 +[#1433]: https://github.com/actix/actix-web/pull/1433 +[#1452]: https://github.com/actix/actix-web/pull/1452 [#1486]: https://github.com/actix/actix-web/pull/1486 @@ -503,16 +503,16 @@ * Add convenience functions `test::read_body_json()` and `test::TestRequest::send_request()` for testing. ### Changed -- -- Use `sha-1` crate instead of unmaintained `sha1` crate + +* Use `sha-1` crate instead of unmaintained `sha1` crate * Skip empty chunks when returning response from a `Stream` [#1308] * Update the `time` dependency to 0.2.7 * Update `actix-tls` dependency to 2.0.0-alpha.1 -- Update `rustls` dependency to 0.17 -- -- 1308]: https://github.com/actix/actix-web/pull/1308 -- -- [2.0.0] - 2019-12-25 +* Update `rustls` dependency to 0.17 + +[#1308]: https://github.com/actix/actix-web/pull/1308 + +## [2.0.0] - 2019-12-25 ### Changed @@ -520,405 +520,404 @@ * Allow to gracefully stop test server via `TestServer::stop()` -- Allow to specify multi-patterns for resources +* Allow to specify multi-patterns for resources -- [2.0.0-rc] - 2019-12-20 +## [2.0.0-rc] - 2019-12-20 -- # Changed +### Changed * Move `BodyEncoding` to `dev` module #1220 * Allow to set `peer_addr` for TestRequest #1074 -- Make web::Data deref to Arc #1214 +* Make web::Data deref to Arc #1214 -- Rename `App::register_data()` to `App::app_data()` +* Rename `App::register_data()` to `App::app_data()` -- `HttpRequest::app_data()` returns `Option<&T>` instead of `Option<&Data>` +* `HttpRequest::app_data()` returns `Option<&T>` instead of `Option<&Data>` -- # Fixed +### Fixed -- Fix `AppConfig::secure()` is always false. #1202 +* Fix `AppConfig::secure()` is always false. #1202 ## [2.0.0-alpha.6] - 2019-12-15 -- + ### Fixed * Fixed compilation with default features off ## [2.0.0-alpha.5] - 2019-12-13 -- # Added +### Added * Add test server, `test::start()` and `test::start_with()` ## [2.0.0-alpha.4] - 2019-12-08 -- # Deleted +### Deleted * Delete HttpServer::run(), it is not useful with async/await ## [2.0.0-alpha.3] - 2019-12-07 -- # Changed +### Changed * Migrate to tokio 0.2 ## [2.0.0-alpha.1] - 2019-11-22 -- + ### Changed * Migrated to `std::future` * Remove implementation of `Responder` for `()`. (#1167) -- + ## [1.0.9] - 2019-11-14 -- + ### Added * Add `Payload::into_inner` method and make stored `def::Payload` public. (#1110) ### Changed -- Support `Host` guards when the `Host` header is unset (e.g. HTTP/2 requests) (#1129) +* Support `Host` guards when the `Host` header is unset (e.g. HTTP/2 requests) (#1129) ## [1.0.8] - 2019-09-25 -- + ### Added * Add `Scope::register_data` and `Resource::register_data` methods, parallel to `App::register_data`. * Add `middleware::Condition` that conditionally enables another middleware -- + * Allow to re-construct `ServiceRequest` from `HttpRequest` and `Payload` -- Add `HttpServer::listen_uds` for ability to listen on UDS FD rather than path, +* Add `HttpServer::listen_uds` for ability to listen on UDS FD rather than path, which is useful for example with systemd. -- + ### Changed -- + * Make UrlEncodedError::Overflow more informative * Use actix-testing for testing utils -- + ## [1.0.7] - 2019-08-29 -- + ### Fixed * Request Extensions leak #1062 ## [1.0.6] - 2019-08-28 -- + ### Added * Re-implement Host predicate (#989) * Form implements Responder, returning a `application/x-www-form-urlencoded` response -- Add `into_inner` to `Data` +* Add `into_inner` to `Data` -- Add `test::TestRequest::set_form()` convenience method to automatically serialize data and set +* Add `test::TestRequest::set_form()` convenience method to automatically serialize data and set the header in test requests. -- + ### Changed -- + * `Query` payload made `pub`. Allows user to pattern-match the payload. * Enable `rust-tls` feature for client #1045 -- Update serde_urlencoded to 0.6.1 +* Update serde_urlencoded to 0.6.1 + +* Update url to 2.1 -- Update url to 2.1 -- ## [1.0.5] - 2019-07-18 -- + ### Added * Unix domain sockets (HttpServer::bind_uds) #92 * Actix now logs errors resulting in "internal server error" responses always, with the `error` logging level -- + ### Fixed -- + * Restored logging of errors through the `Logger` middleware ## [1.0.4] - 2019-07-17 -- + ### Added * Add `Responder` impl for `(T, StatusCode) where T: Responder` * Allow to access app's resource map via `ServiceRequest::resource_map()` and `HttpRequest::resource_map()` methods. -- + ### Changed -- + * Upgrade `rand` dependency version to 0.7 ## [1.0.3] - 2019-06-28 -- + ### Added * Support asynchronous data factories #850 ### Changed -- Use `encoding_rs` crate instead of unmaintained `encoding` crate +* Use `encoding_rs` crate instead of unmaintained `encoding` crate ## [1.0.2] - 2019-06-17 -- + ### Changed * Move cors middleware to `actix-cors` crate. * Move identity middleware to `actix-identity` crate. -- + ## [1.0.1] - 2019-06-17 -- + ### Added * Add support for PathConfig #903 * Add `middleware::identity::RequestIdentity` trait to `get_identity` from `HttpMessage`. -- # Changed +### Changed -- Move cors middleware to `actix-cors` crate. +* Move cors middleware to `actix-cors` crate. * Move identity middleware to `actix-identity` crate. -- Disable default feature `secure-cookies`. +* Disable default feature `secure-cookies`. -- Allow to test an app that uses async actors #897 +* Allow to test an app that uses async actors #897 -- Re-apply patch from #637 #894 +* Re-apply patch from #637 #894 -- # Fixed +### Fixed -- HttpRequest::url_for is broken with nested scopes #915 +* HttpRequest::url_for is broken with nested scopes #915 ## [1.0.0] - 2019-06-05 -- + ### Added * Add `Scope::configure()` method. * Add `ServiceRequest::set_payload()` method. -- Add `test::TestRequest::set_json()` convenience method to automatically +* Add `test::TestRequest::set_json()` convenience method to automatically serialize data and set header in test requests. -- + * Add macros for head, options, trace, connect and patch http methods -- + ### Changed -- Drop an unnecessary `Option<_>` indirection around `ServerBuilder` from `HttpServer`. #863 +* Drop an unnecessary `Option<_>` indirection around `ServerBuilder` from `HttpServer`. #863 ### Fixed -- Fix Logger request time format, and use rfc3339. #867 +* Fix Logger request time format, and use rfc3339. #867 * Clear http requests pool on app service drop #860 -- + ## [1.0.0-rc] - 2019-05-18 -- + ### Added * Add `Query::from_query()` to extract parameters from a query string. #846 * `QueryConfig`, similar to `JsonConfig` for customizing error handling of query extractors. ### Changed -- -- `JsonConfig` is now `Send + Sync`, this implies that `error_handler` must be `Send + Sync` too. + +* `JsonConfig` is now `Send + Sync`, this implies that `error_handler` must be `Send + Sync` too. ### Fixed -- Codegen with parameters in the path only resolves the first registered endpoint #841 +* Codegen with parameters in the path only resolves the first registered endpoint #841 ## [1.0.0-beta.4] - 2019-05-12 -- + ### Added * Allow to set/override app data on scope level ### Changed -- `App::configure` take an `FnOnce` instead of `Fn` +* `App::configure` take an `FnOnce` instead of `Fn` * Upgrade actix-net crates -- [1.0.0-beta.3] - 2019-05-04 -- +## [1.0.0-beta.3] - 2019-05-04 + ### Added * Add helper function for executing futures `test::block_fn()` ### Changed -- Extractor configuration could be registered with `App::data()` +* Extractor configuration could be registered with `App::data()` or with `Resource::data()` #775 * Route data is unified with app data, `Route::data()` moved to resource -- level to `Resource::data()` + level to `Resource::data()` * CORS handling without headers #702 -- + * Allow constructing `Data` instances to avoid double `Arc` for `Send + Sync` types. -- # Fixed +### Fixed -- Fix `NormalizePath` middleware impl #806 +* Fix `NormalizePath` middleware impl #806 ### Deleted -- `App::data_factory()` is deleted. +* `App::data_factory()` is deleted. ## [1.0.0-beta.2] - 2019-04-24 -- + ### Added * Add raw services support via `web::service()` * Add helper functions for reading response body `test::read_body()` -- Add support for `remainder match` (i.e "/path/{tail}*") +* Add support for `remainder match` (i.e "/path/{tail}*") -- Extend `Responder` trait, allow to override status code and headers. +* Extend `Responder` trait, allow to override status code and headers. -- Store visit and login timestamp in the identity cookie #502 +* Store visit and login timestamp in the identity cookie #502 -- # Changed +### Changed -- `.to_async()` handler can return `Responder` type #792 +* `.to_async()` handler can return `Responder` type #792 ### Fixed -- Fix async web::Data factory handling +* Fix async web::Data factory handling ## [1.0.0-beta.1] - 2019-04-20 -- + ### Added * Add helper functions for reading test response body, `test::read_response()` and test::read_response_json()` * Add `.peer_addr()` #744 -- + * Add `NormalizePath` middleware -- # Changed +### Changed -- Rename `RouterConfig` to `ServiceConfig` +* Rename `RouterConfig` to `ServiceConfig` * Rename `test::call_success` to `test::call_service` -- Removed `ServiceRequest::from_parts()` as it is unsafe to create from parts. +* Removed `ServiceRequest::from_parts()` as it is unsafe to create from parts. -- `CookieIdentityPolicy::max_age()` accepts value in seconds +* `CookieIdentityPolicy::max_age()` accepts value in seconds -- # Fixed +### Fixed -- Fixed `TestRequest::app_data()` +* Fixed `TestRequest::app_data()` ## [1.0.0-alpha.6] - 2019-04-14 -- + ### Changed * Allow using any service as default service. * Remove generic type for request payload, always use default. -- Removed `Decompress` middleware. Bytes, String, Json, Form extractors +* Removed `Decompress` middleware. Bytes, String, Json, Form extractors automatically decompress payload. -- + * Make extractor config type explicit. Add `FromRequest::Config` associated type. -- + ## [1.0.0-alpha.5] - 2019-04-12 -- + ### Added * Added async io `TestBuffer` for testing. ### Deleted -- Removed native-tls support +* Removed native-tls support ## [1.0.0-alpha.4] - 2019-04-08 -- + ### Added * `App::configure()` allow to offload app configuration to different methods * Added `URLPath` option for logger -- Added `ServiceRequest::app_data()`, returns `Data` +* Added `ServiceRequest::app_data()`, returns `Data` -- Added `ServiceFromRequest::app_data()`, returns `Data` +* Added `ServiceFromRequest::app_data()`, returns `Data` -- # Changed +### Changed -- `FromRequest` trait refactoring +* `FromRequest` trait refactoring * Move multipart support to actix-multipart crate -- # Fixed +### Fixed -- Fix body propagation in Response::from_error. #760 +* Fix body propagation in Response::from_error. #760 ## [1.0.0-alpha.3] - 2019-04-02 -- + ### Changed * Renamed `TestRequest::to_service()` to `TestRequest::to_srv_request()` * Renamed `TestRequest::to_response()` to `TestRequest::to_srv_response()` -- Removed `Deref` impls +* Removed `Deref` impls -- # Removed +### Removed -- Removed unused `actix_web::web::md()` +* Removed unused `actix_web::web::md()` ## [1.0.0-alpha.2] - 2019-03-29 -- + ### Added * Rustls support ### Changed -- Use forked cookie +* Use forked cookie * Multipart::Field renamed to MultipartField -- [1.0.0-alpha.1] - 2019-03-28 +## [1.0.0-alpha.1] - 2019-03-28 -- # Changed +### Changed * Complete architecture re-design. * Return 405 response if no matching route found within resource #538 -- - diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index ef8eba0fc..d6b39e28f 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -4,42 +4,42 @@ ## 0.6.0-beta.10 - 2021-12-11 -- No significant changes since `0.6.0-beta.9`. +* No significant changes since `0.6.0-beta.9`. ## 0.6.0-beta.9 - 2021-11-22 -- Add crate feature `experimental-io-uring`, enabling async file I/O to be utilized. This feature is only available on Linux OSes with recent kernel versions. This feature is semver-exempt. [#2408] -- Add `NamedFile::open_async`. [#2408] -- Fix 304 Not Modified responses to omit the Content-Length header, as per the spec. [#2453] -- The `Responder` impl for `NamedFile` now has a boxed future associated type. [#2408] -- The `Service` impl for `NamedFileService` now has a boxed future associated type. [#2408] -- Add `impl Clone` for `FilesService`. [#2408] +* Add crate feature `experimental-io-uring`, enabling async file I/O to be utilized. This feature is only available on Linux OSes with recent kernel versions. This feature is semver-exempt. [#2408] +* Add `NamedFile::open_async`. [#2408] +* Fix 304 Not Modified responses to omit the Content-Length header, as per the spec. [#2453] +* The `Responder` impl for `NamedFile` now has a boxed future associated type. [#2408] +* The `Service` impl for `NamedFileService` now has a boxed future associated type. [#2408] +* Add `impl Clone` for `FilesService`. [#2408] [#2408]: https://github.com/actix/actix-web/pull/2408 [#2453]: https://github.com/actix/actix-web/pull/2453 ## 0.6.0-beta.8 - 2021-10-20 -- Minimum supported Rust version (MSRV) is now 1.52. +* Minimum supported Rust version (MSRV) is now 1.52. ## 0.6.0-beta.7 - 2021-09-09 -- Minimum supported Rust version (MSRV) is now 1.51. +* Minimum supported Rust version (MSRV) is now 1.51. ## 0.6.0-beta.6 - 2021-06-26 -- Added `Files::path_filter()`. [#2274] -- `Files::show_files_listing()` can now be used with `Files::index_file()` to show files listing as a fallback when the index file is not found. [#2228] +* Added `Files::path_filter()`. [#2274] +* `Files::show_files_listing()` can now be used with `Files::index_file()` to show files listing as a fallback when the index file is not found. [#2228] [#2274]: https://github.com/actix/actix-web/pull/2274 [#2228]: https://github.com/actix/actix-web/pull/2228 ## 0.6.0-beta.5 - 2021-06-17 -- `NamedFile` now implements `ServiceFactory` and `HttpServiceFactory` making it much more useful in routing. For example, it can be used directly as a default service. [#2135] -- For symbolic links, `Content-Disposition` header no longer shows the filename of the original file. [#2156] -- `Files::redirect_to_slash_directory()` now works as expected when used with `Files::show_files_listing()`. [#2225] -- `application/{javascript, json, wasm}` mime type now have `inline` disposition by default. [#2257] +* `NamedFile` now implements `ServiceFactory` and `HttpServiceFactory` making it much more useful in routing. For example, it can be used directly as a default service. [#2135] +* For symbolic links, `Content-Disposition` header no longer shows the filename of the original file. [#2156] +* `Files::redirect_to_slash_directory()` now works as expected when used with `Files::show_files_listing()`. [#2225] +* `application/{javascript, json, wasm}` mime type now have `inline` disposition by default. [#2257] [#2135]: https://github.com/actix/actix-web/pull/2135 [#2156]: https://github.com/actix/actix-web/pull/2156 @@ -48,130 +48,130 @@ ## 0.6.0-beta.4 - 2021-04-02 -- Add support for `.guard` in `Files` to selectively filter `Files` services. [#2046] +* Add support for `.guard` in `Files` to selectively filter `Files` services. [#2046] [#2046]: https://github.com/actix/actix-web/pull/2046 ## 0.6.0-beta.3 - 2021-03-09 -- No notable changes. +* No notable changes. ## 0.6.0-beta.2 - 2021-02-10 -- Fix If-Modified-Since and If-Unmodified-Since to not compare using sub-second timestamps. [#1887] -- Replace `v_htmlescape` with `askama_escape`. [#1953] +* Fix If-Modified-Since and If-Unmodified-Since to not compare using sub-second timestamps. [#1887] +* Replace `v_htmlescape` with `askama_escape`. [#1953] [#1887]: https://github.com/actix/actix-web/pull/1887 [#1953]: https://github.com/actix/actix-web/pull/1953 ## 0.6.0-beta.1 - 2021-01-07 -- `HttpRange::parse` now has its own error type. -- Update `bytes` to `1.0`. [#1813] +* `HttpRange::parse` now has its own error type. +* Update `bytes` to `1.0`. [#1813] [#1813]: https://github.com/actix/actix-web/pull/1813 ## 0.5.0 - 2020-12-26 -- Optionally support hidden files/directories. [#1811] +* Optionally support hidden files/directories. [#1811] [#1811]: https://github.com/actix/actix-web/pull/1811 ## 0.4.1 - 2020-11-24 -- Clarify order of parameters in `Files::new` and improve docs. +* Clarify order of parameters in `Files::new` and improve docs. ## 0.4.0 - 2020-10-06 -- Add `Files::prefer_utf8` option that adds UTF-8 charset on certain response types. [#1714] +* Add `Files::prefer_utf8` option that adds UTF-8 charset on certain response types. [#1714] [#1714]: https://github.com/actix/actix-web/pull/1714 ## 0.3.0 - 2020-09-11 -- No significant changes from 0.3.0-beta.1. +* No significant changes from 0.3.0-beta.1. ## 0.3.0-beta.1 - 2020-07-15 -- Update `v_htmlescape` to 0.10 -- Update `actix-web` and `actix-http` dependencies to beta.1 +* Update `v_htmlescape` to 0.10 +* Update `actix-web` and `actix-http` dependencies to beta.1 ## 0.3.0-alpha.1 - 2020-05-23 -- Update `actix-web` and `actix-http` dependencies to alpha -- Fix some typos in the docs -- Bump minimum supported Rust version to 1.40 -- Support sending Content-Length when Content-Range is specified [#1384] +* Update `actix-web` and `actix-http` dependencies to alpha +* Fix some typos in the docs +* Bump minimum supported Rust version to 1.40 +* Support sending Content-Length when Content-Range is specified [#1384] [#1384]: https://github.com/actix/actix-web/pull/1384 ## 0.2.1 - 2019-12-22 -- Use the same format for file URLs regardless of platforms +* Use the same format for file URLs regardless of platforms ## 0.2.0 - 2019-12-20 -- Fix BodyEncoding trait import #1220 +* Fix BodyEncoding trait import #1220 ## 0.2.0-alpha.1 - 2019-12-07 -- Migrate to `std::future` +* Migrate to `std::future` ## 0.1.7 - 2019-11-06 -- Add an additional `filename*` param in the `Content-Disposition` header of +* Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151) ## 0.1.6 - 2019-10-14 -- Add option to redirect to a slash-ended path `Files` #1132 +* Add option to redirect to a slash-ended path `Files` #1132 ## 0.1.5 - 2019-10-08 -- Bump up `mime_guess` crate version to 2.0.1 -- Bump up `percent-encoding` crate version to 2.1 -- Allow user defined request guards for `Files` #1113 +* Bump up `mime_guess` crate version to 2.0.1 +* Bump up `percent-encoding` crate version to 2.1 +* Allow user defined request guards for `Files` #1113 ## 0.1.4 - 2019-07-20 -- Allow to disable `Content-Disposition` header #686 +* Allow to disable `Content-Disposition` header #686 ## 0.1.3 - 2019-06-28 -- Do not set `Content-Length` header, let actix-http set it #930 +* Do not set `Content-Length` header, let actix-http set it #930 ## 0.1.2 - 2019-06-13 -- Content-Length is 0 for NamedFile HEAD request #914 -- Fix ring dependency from actix-web default features for #741 +* Content-Length is 0 for NamedFile HEAD request #914 +* Fix ring dependency from actix-web default features for #741 ## 0.1.1 - 2019-06-01 -- Static files are incorrectly served as both chunked and with length #812 +* Static files are incorrectly served as both chunked and with length #812 ## 0.1.0 - 2019-05-25 -- NamedFile last-modified check always fails due to nano-seconds in file modified date #820 +* NamedFile last-modified check always fails due to nano-seconds in file modified date #820 ## 0.1.0-beta.4 - 2019-05-12 -- Update actix-web to beta.4 +* Update actix-web to beta.4 ## 0.1.0-beta.1 - 2019-04-20 -- Update actix-web to beta.1 +* Update actix-web to beta.1 ## 0.1.0-alpha.6 - 2019-04-14 -- Update actix-web to alpha6 +* Update actix-web to alpha6 ## 0.1.0-alpha.4 - 2019-04-08 -- Update actix-web to alpha4 +* Update actix-web to alpha4 ## 0.1.0-alpha.2 - 2019-04-02 -- Add default handler support +* Add default handler support ## 0.1.0-alpha.1 - 2019-03-28 -- Initial impl +* Initial impl diff --git a/actix-http-test/CHANGES.md b/actix-http-test/CHANGES.md index 4e86e20e8..156012168 100644 --- a/actix-http-test/CHANGES.md +++ b/actix-http-test/CHANGES.md @@ -4,125 +4,125 @@ ## 3.0.0-beta.9 - 2021-12-11 -- No significant changes since `3.0.0-beta.8`. +* No significant changes since `3.0.0-beta.8`. ## 3.0.0-beta.8 - 2021-11-30 -- Update `actix-tls` to `3.0.0-rc.1`. [#2474] +* Update `actix-tls` to `3.0.0-rc.1`. [#2474] [#2474]: https://github.com/actix/actix-web/pull/2474 ## 3.0.0-beta.7 - 2021-11-22 -- Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408] +* Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408] [#2408]: https://github.com/actix/actix-web/pull/2408 ## 3.0.0-beta.6 - 2021-11-15 -- `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442] -- Update `actix-server` to `2.0.0-beta.9`. [#2442] -- Minimum supported Rust version (MSRV) is now 1.52. +* `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442] +* Update `actix-server` to `2.0.0-beta.9`. [#2442] +* Minimum supported Rust version (MSRV) is now 1.52. [#2442]: https://github.com/actix/actix-web/pull/2442 ## 3.0.0-beta.5 - 2021-09-09 -- Minimum supported Rust version (MSRV) is now 1.51. +* Minimum supported Rust version (MSRV) is now 1.51. ## 3.0.0-beta.4 - 2021-04-02 -- Added `TestServer::client_headers` method. [#2097] +* Added `TestServer::client_headers` method. [#2097] [#2097]: https://github.com/actix/actix-web/pull/2097 ## 3.0.0-beta.3 - 2021-03-09 -- No notable changes. +* No notable changes. ## 3.0.0-beta.2 - 2021-02-10 -- No notable changes. +* No notable changes. ## 3.0.0-beta.1 - 2021-01-07 -- Update `bytes` to `1.0`. [#1813] +* Update `bytes` to `1.0`. [#1813] [#1813]: https://github.com/actix/actix-web/pull/1813 ## 2.1.0 - 2020-11-25 -- Add ability to set address for `TestServer`. [#1645] -- Upgrade `base64` to `0.13`. -- Upgrade `serde_urlencoded` to `0.7`. [#1773] +* Add ability to set address for `TestServer`. [#1645] +* Upgrade `base64` to `0.13`. +* Upgrade `serde_urlencoded` to `0.7`. [#1773] [#1773]: https://github.com/actix/actix-web/pull/1773 [#1645]: https://github.com/actix/actix-web/pull/1645 ## 2.0.0 - 2020-09-11 -- Update actix-codec and actix-utils dependencies. +* Update actix-codec and actix-utils dependencies. ## 2.0.0-alpha.1 - 2020-05-23 -- Update the `time` dependency to 0.2.7 -- Update `actix-connect` dependency to 2.0.0-alpha.2 -- Make `test_server` `async` fn. -- Bump minimum supported Rust version to 1.40 -- Replace deprecated `net2` crate with `socket2` -- Update `base64` dependency to 0.12 -- Update `env_logger` dependency to 0.7 +* Update the `time` dependency to 0.2.7 +* Update `actix-connect` dependency to 2.0.0-alpha.2 +* Make `test_server` `async` fn. +* Bump minimum supported Rust version to 1.40 +* Replace deprecated `net2` crate with `socket2` +* Update `base64` dependency to 0.12 +* Update `env_logger` dependency to 0.7 ## 1.0.0 - 2019-12-13 -- Replaced `TestServer::start()` with `test_server()` +* Replaced `TestServer::start()` with `test_server()` ## 1.0.0-alpha.3 - 2019-12-07 -- Migrate to `std::future` +* Migrate to `std::future` ## 0.2.5 - 2019-09-17 -- Update serde_urlencoded to "0.6.1" -- Increase TestServerRuntime timeouts from 500ms to 3000ms -- Do not override current `System` +* Update serde_urlencoded to "0.6.1" +* Increase TestServerRuntime timeouts from 500ms to 3000ms +* Do not override current `System` ## 0.2.4 - 2019-07-18 -- Update actix-server to 0.6 +* Update actix-server to 0.6 ## 0.2.3 - 2019-07-16 -- Add `delete`, `options`, `patch` methods to `TestServerRunner` +* Add `delete`, `options`, `patch` methods to `TestServerRunner` ## 0.2.2 - 2019-06-16 -- Add .put() and .sput() methods +* Add .put() and .sput() methods ## 0.2.1 - 2019-06-05 -- Add license files +* Add license files ## 0.2.0 - 2019-05-12 -- Update awc and actix-http deps +* Update awc and actix-http deps ## 0.1.1 - 2019-04-24 -- Always make new connection for http client +* Always make new connection for http client ## 0.1.0 - 2019-04-16 -- No changes +* No changes ## 0.1.0-alpha.3 - 2019-04-02 -- Request functions accept path #743 +* Request functions accept path #743 ## 0.1.0-alpha.2 - 2019-03-29 -- Added TestServerRuntime::load_body() method -- Update actix-http and awc libraries +* Added TestServerRuntime::load_body() method +* Update actix-http and awc libraries ## 0.1.0-alpha.1 - 2019-03-28 -- Initial impl +* Initial impl diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 3b45e934f..ad98d132a 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -2,22 +2,22 @@ ## Unreleased - 2021-xx-xx ### Changes -- `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527] +* `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527] [#2527]: https://github.com/actix/actix-web/pull/2527 ## 3.0.0-beta.16 - 2021-12-17 ### Added -- New method on `MessageBody` trait, `try_into_bytes`, with default implementation, for optimizations on body types that complete in exactly one poll. Replaces `is_complete_body` and `take_complete_body`. [#2522] +* New method on `MessageBody` trait, `try_into_bytes`, with default implementation, for optimizations on body types that complete in exactly one poll. Replaces `is_complete_body` and `take_complete_body`. [#2522] ### Changed -- Rename trait `IntoHeaderPair => TryIntoHeaderPair`. [#2510] -- Rename `TryIntoHeaderPair::{try_into_header_pair => try_into_pair}`. [#2510] -- Rename trait `IntoHeaderValue => TryIntoHeaderValue`. [#2510] +* Rename trait `IntoHeaderPair => TryIntoHeaderPair`. [#2510] +* Rename `TryIntoHeaderPair::{try_into_header_pair => try_into_pair}`. [#2510] +* Rename trait `IntoHeaderValue => TryIntoHeaderValue`. [#2510] ### Removed -- `MessageBody::{is_complete_body,take_complete_body}`. [#2522] +* `MessageBody::{is_complete_body,take_complete_body}`. [#2522] [#2510]: https://github.com/actix/actix-web/pull/2510 [#2522]: https://github.com/actix/actix-web/pull/2522 @@ -25,43 +25,43 @@ ## 3.0.0-beta.15 - 2021-12-11 ### Added -- Add timeout for canceling HTTP/2 server side connection handshake. Default to 5 seconds. [#2483] -- HTTP/2 handshake timeout can be configured with `ServiceConfig::client_timeout`. [#2483] -- `Response::map_into_boxed_body`. [#2468] -- `body::EitherBody` enum. [#2468] -- `body::None` struct. [#2468] -- Impl `MessageBody` for `bytestring::ByteString`. [#2468] -- `impl Clone for ws::HandshakeError`. [#2468] -- `#[must_use]` for `ws::Codec` to prevent subtle bugs. [#1920] -- `impl Default ` for `ws::Codec`. [#1920] -- `header::QualityItem::{max, min}`. [#2486] -- `header::Quality::{MAX, MIN}`. [#2486] -- `impl Display` for `header::Quality`. [#2486] -- Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491] -- `Request::take_conn_data()`. [#2491] -- `Request::take_req_data()`. [#2487] -- `impl Clone` for `RequestHead`. [#2487] -- New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimizations on body types that are done in exactly one poll/chunk. [#2497] -- New `boxed` method on `MessageBody` trait for wrapping body type. [#2520] +* Add timeout for canceling HTTP/2 server side connection handshake. Default to 5 seconds. [#2483] +* HTTP/2 handshake timeout can be configured with `ServiceConfig::client_timeout`. [#2483] +* `Response::map_into_boxed_body`. [#2468] +* `body::EitherBody` enum. [#2468] +* `body::None` struct. [#2468] +* Impl `MessageBody` for `bytestring::ByteString`. [#2468] +* `impl Clone for ws::HandshakeError`. [#2468] +* `#[must_use]` for `ws::Codec` to prevent subtle bugs. [#1920] +* `impl Default ` for `ws::Codec`. [#1920] +* `header::QualityItem::{max, min}`. [#2486] +* `header::Quality::{MAX, MIN}`. [#2486] +* `impl Display` for `header::Quality`. [#2486] +* Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491] +* `Request::take_conn_data()`. [#2491] +* `Request::take_req_data()`. [#2487] +* `impl Clone` for `RequestHead`. [#2487] +* New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimizations on body types that are done in exactly one poll/chunk. [#2497] +* New `boxed` method on `MessageBody` trait for wrapping body type. [#2520] ### Changed -- Rename `body::BoxBody::{from_body => new}`. [#2468] -- Body type for `Responses` returned from `Response::{new, ok, etc...}` is now `BoxBody`. [#2468] -- The `Error` associated type on `MessageBody` type now requires `impl Error` (or similar). [#2468] -- Error types using in service builders now require `Into>`. [#2468] -- `From` implementations on error types now return a `Response`. [#2468] -- `ResponseBuilder::body(B)` now returns `Response>`. [#2468] -- `ResponseBuilder::finish()` now returns `Response>`. [#2468] +* Rename `body::BoxBody::{from_body => new}`. [#2468] +* Body type for `Responses` returned from `Response::{new, ok, etc...}` is now `BoxBody`. [#2468] +* The `Error` associated type on `MessageBody` type now requires `impl Error` (or similar). [#2468] +* Error types using in service builders now require `Into>`. [#2468] +* `From` implementations on error types now return a `Response`. [#2468] +* `ResponseBuilder::body(B)` now returns `Response>`. [#2468] +* `ResponseBuilder::finish()` now returns `Response>`. [#2468] ### Removed -- `ResponseBuilder::streaming`. [#2468] -- `impl Future` for `ResponseBuilder`. [#2468] -- Remove unnecessary `MessageBody` bound on types passed to `body::AnyBody::new`. [#2468] -- Move `body::AnyBody` to `awc`. Replaced with `EitherBody` and `BoxBody`. [#2468] -- `impl Copy` for `ws::Codec`. [#1920] -- `header::qitem` helper. Replaced with `header::QualityItem::max`. [#2486] -- `impl TryFrom` for `header::Quality`. [#2486] -- `http` module. Most everything it contained is exported at the crate root. [#2488] +* `ResponseBuilder::streaming`. [#2468] +* `impl Future` for `ResponseBuilder`. [#2468] +* Remove unnecessary `MessageBody` bound on types passed to `body::AnyBody::new`. [#2468] +* Move `body::AnyBody` to `awc`. Replaced with `EitherBody` and `BoxBody`. [#2468] +* `impl Copy` for `ws::Codec`. [#1920] +* `header::qitem` helper. Replaced with `header::QualityItem::max`. [#2486] +* `impl TryFrom` for `header::Quality`. [#2486] +* `http` module. Most everything it contained is exported at the crate root. [#2488] [#2483]: https://github.com/actix/actix-web/pull/2483 [#2468]: https://github.com/actix/actix-web/pull/2468 @@ -76,10 +76,10 @@ ## 3.0.0-beta.14 - 2021-11-30 ### Changed -- Guarantee ordering of `header::GetAll` iterator to be same as insertion order. [#2467] -- Expose `header::map` module. [#2467] -- Implement `ExactSizeIterator` and `FusedIterator` for all `HeaderMap` iterators. [#2470] -- Update `actix-tls` to `3.0.0-rc.1`. [#2474] +* Guarantee ordering of `header::GetAll` iterator to be same as insertion order. [#2467] +* Expose `header::map` module. [#2467] +* Implement `ExactSizeIterator` and `FusedIterator` for all `HeaderMap` iterators. [#2470] +* Update `actix-tls` to `3.0.0-rc.1`. [#2474] [#2467]: https://github.com/actix/actix-web/pull/2467 [#2470]: https://github.com/actix/actix-web/pull/2470 @@ -88,24 +88,24 @@ ## 3.0.0-beta.13 - 2021-11-22 ### Added -- `body::AnyBody::empty` for quickly creating an empty body. [#2446] -- `body::AnyBody::none` for quickly creating a "none" body. [#2456] -- `impl Clone` for `body::AnyBody where S: Clone`. [#2448] -- `body::AnyBody::into_boxed` for quickly converting to a type-erased, boxed body type. [#2448] +* `body::AnyBody::empty` for quickly creating an empty body. [#2446] +* `body::AnyBody::none` for quickly creating a "none" body. [#2456] +* `impl Clone` for `body::AnyBody where S: Clone`. [#2448] +* `body::AnyBody::into_boxed` for quickly converting to a type-erased, boxed body type. [#2448] ### Changed -- Rename `body::AnyBody::{Message => Body}`. [#2446] -- Rename `body::AnyBody::{from_message => new_boxed}`. [#2448] -- Rename `body::AnyBody::{from_slice => copy_from_slice}`. [#2448] -- Rename `body::{BoxAnyBody => BoxBody}`. [#2448] -- Change representation of `AnyBody` to include a type parameter in `Body` variant. Defaults to `BoxBody`. [#2448] -- `Encoder::response` now returns `AnyBody>`. [#2448] +* Rename `body::AnyBody::{Message => Body}`. [#2446] +* Rename `body::AnyBody::{from_message => new_boxed}`. [#2448] +* Rename `body::AnyBody::{from_slice => copy_from_slice}`. [#2448] +* Rename `body::{BoxAnyBody => BoxBody}`. [#2448] +* Change representation of `AnyBody` to include a type parameter in `Body` variant. Defaults to `BoxBody`. [#2448] +* `Encoder::response` now returns `AnyBody>`. [#2448] ### Removed -- `body::AnyBody::Empty`; an empty body can now only be represented as a zero-length `Bytes` variant. [#2446] -- `body::BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446] -- `EncoderError::Boxed`; it is no longer required. [#2446] -- `body::ResponseBody`; is function is replaced by the new `body::AnyBody` enum. [#2446] +* `body::AnyBody::Empty`; an empty body can now only be represented as a zero-length `Bytes` variant. [#2446] +* `body::BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446] +* `EncoderError::Boxed`; it is no longer required. [#2446] +* `body::ResponseBody`; is function is replaced by the new `body::AnyBody` enum. [#2446] [#2446]: https://github.com/actix/actix-web/pull/2446 [#2448]: https://github.com/actix/actix-web/pull/2448 @@ -114,11 +114,11 @@ ## 3.0.0-beta.12 - 2021-11-15 ### Changed -- Update `actix-server` to `2.0.0-beta.9`. [#2442] +* Update `actix-server` to `2.0.0-beta.9`. [#2442] ### Removed -- `client` module. [#2425] -- `trust-dns` feature. [#2425] +* `client` module. [#2425] +* `trust-dns` feature. [#2425] [#2425]: https://github.com/actix/actix-web/pull/2425 [#2442]: https://github.com/actix/actix-web/pull/2442 @@ -126,21 +126,21 @@ ## 3.0.0-beta.11 - 2021-10-20 ### Changed -- Updated rustls to v0.20. [#2414] -- Minimum supported Rust version (MSRV) is now 1.52. +* Updated rustls to v0.20. [#2414] +* Minimum supported Rust version (MSRV) is now 1.52. [#2414]: https://github.com/actix/actix-web/pull/2414 ## 3.0.0-beta.10 - 2021-09-09 ### Changed -- `ContentEncoding` is now marked `#[non_exhaustive]`. [#2377] -- Minimum supported Rust version (MSRV) is now 1.51. +* `ContentEncoding` is now marked `#[non_exhaustive]`. [#2377] +* Minimum supported Rust version (MSRV) is now 1.51. ### Fixed -- Remove slice creation pointing to potential uninitialized data on h1 encoder. [#2364] -- Remove `Into` bound on `Encoder` body types. [#2375] -- Fix quality parse error in Accept-Encoding header. [#2344] +* Remove slice creation pointing to potential uninitialized data on h1 encoder. [#2364] +* Remove `Into` bound on `Encoder` body types. [#2375] +* Fix quality parse error in Accept-Encoding header. [#2344] [#2364]: https://github.com/actix/actix-web/pull/2364 [#2375]: https://github.com/actix/actix-web/pull/2375 @@ -150,15 +150,15 @@ ## 3.0.0-beta.9 - 2021-08-09 ### Fixed -- Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977) +* Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977) ## 3.0.0-beta.8 - 2021-06-26 ### Changed -- Change compression algorithm features flags. [#2250] +* Change compression algorithm features flags. [#2250] ### Removed -- `downcast` and `downcast_get_type_id` macros. [#2291] +* `downcast` and `downcast_get_type_id` macros. [#2291] [#2291]: https://github.com/actix/actix-web/pull/2291 [#2250]: https://github.com/actix/actix-web/pull/2250 @@ -166,37 +166,37 @@ ## 3.0.0-beta.7 - 2021-06-17 ### Added -- Alias `body::Body` as `body::AnyBody`. [#2215] -- `BoxAnyBody`: a boxed message body with boxed errors. [#2183] -- Re-export `http` crate's `Error` type as `error::HttpError`. [#2171] -- Re-export `StatusCode`, `Method`, `Version` and `Uri` at the crate root. [#2171] -- Re-export `ContentEncoding` and `ConnectionType` at the crate root. [#2171] -- `Response::into_body` that consumes response and returns body type. [#2201] -- `impl Default` for `Response`. [#2201] -- Add zstd support for `ContentEncoding`. [#2244] +* Alias `body::Body` as `body::AnyBody`. [#2215] +* `BoxAnyBody`: a boxed message body with boxed errors. [#2183] +* Re-export `http` crate's `Error` type as `error::HttpError`. [#2171] +* Re-export `StatusCode`, `Method`, `Version` and `Uri` at the crate root. [#2171] +* Re-export `ContentEncoding` and `ConnectionType` at the crate root. [#2171] +* `Response::into_body` that consumes response and returns body type. [#2201] +* `impl Default` for `Response`. [#2201] +* Add zstd support for `ContentEncoding`. [#2244] ### Changed -- The `MessageBody` trait now has an associated `Error` type. [#2183] -- All error trait bounds in server service builders have changed from `Into` to `Into>`. [#2253] -- All error trait bounds in message body and stream impls changed from `Into` to `Into>`. [#2253] -- Places in `Response` where `ResponseBody` was received or returned now simply use `B`. [#2201] -- `header` mod is now public. [#2171] -- `uri` mod is now public. [#2171] -- Update `language-tags` to `0.3`. -- Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201] -- `ResponseBuilder::message_body` now returns a `Result`. [#2201] -- Remove `Unpin` bound on `ResponseBuilder::streaming`. [#2253] -- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226] +* The `MessageBody` trait now has an associated `Error` type. [#2183] +* All error trait bounds in server service builders have changed from `Into` to `Into>`. [#2253] +* All error trait bounds in message body and stream impls changed from `Into` to `Into>`. [#2253] +* Places in `Response` where `ResponseBody` was received or returned now simply use `B`. [#2201] +* `header` mod is now public. [#2171] +* `uri` mod is now public. [#2171] +* Update `language-tags` to `0.3`. +* Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201] +* `ResponseBuilder::message_body` now returns a `Result`. [#2201] +* Remove `Unpin` bound on `ResponseBuilder::streaming`. [#2253] +* `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226] ### Removed -- Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171] -- Down-casting for `MessageBody` types. [#2183] -- `error::Result` alias. [#2201] -- Error field from `Response` and `Response::error`. [#2205] -- `impl Future` for `Response`. [#2201] -- `Response::take_body` and old `Response::into_body` method that casted body type. [#2201] -- `InternalError` and all the error types it constructed. [#2215] -- Conversion (`impl Into`) of `Response` and `ResponseBuilder` to `Error`. [#2215] +* Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171] +* Down-casting for `MessageBody` types. [#2183] +* `error::Result` alias. [#2201] +* Error field from `Response` and `Response::error`. [#2205] +* `impl Future` for `Response`. [#2201] +* `Response::take_body` and old `Response::into_body` method that casted body type. [#2201] +* `InternalError` and all the error types it constructed. [#2215] +* Conversion (`impl Into`) of `Response` and `ResponseBuilder` to `Error`. [#2215] [#2171]: https://github.com/actix/actix-web/pull/2171 [#2183]: https://github.com/actix/actix-web/pull/2183 @@ -211,27 +211,27 @@ ## 3.0.0-beta.6 - 2021-04-17 ### Added -- `impl MessageBody for Pin>`. [#2152] -- `Response::{ok, bad_request, not_found, internal_server_error}`. [#2159] -- Helper `body::to_bytes` for async collecting message body into Bytes. [#2158] +* `impl MessageBody for Pin>`. [#2152] +* `Response::{ok, bad_request, not_found, internal_server_error}`. [#2159] +* Helper `body::to_bytes` for async collecting message body into Bytes. [#2158] ### Changes -- The type parameter of `Response` no longer has a default. [#2152] -- The `Message` variant of `body::Body` is now `Pin>`. [#2152] -- `BodyStream` and `SizedStream` are no longer restricted to Unpin types. [#2152] -- Error enum types are marked `#[non_exhaustive]`. [#2161] +* The type parameter of `Response` no longer has a default. [#2152] +* The `Message` variant of `body::Body` is now `Pin>`. [#2152] +* `BodyStream` and `SizedStream` are no longer restricted to Unpin types. [#2152] +* Error enum types are marked `#[non_exhaustive]`. [#2161] ### Removed -- `cookies` feature flag. [#2065] -- Top-level `cookies` mod (re-export). [#2065] -- `HttpMessage` trait loses the `cookies` and `cookie` methods. [#2065] -- `impl ResponseError for CookieParseError`. [#2065] -- Deprecated methods on `ResponseBuilder`: `if_true`, `if_some`. [#2148] -- `ResponseBuilder::json`. [#2148] -- `ResponseBuilder::{set_header, header}`. [#2148] -- `impl From for Body`. [#2148] -- `Response::build_from`. [#2159] -- Most of the status code builders on `Response`. [#2159] +* `cookies` feature flag. [#2065] +* Top-level `cookies` mod (re-export). [#2065] +* `HttpMessage` trait loses the `cookies` and `cookie` methods. [#2065] +* `impl ResponseError for CookieParseError`. [#2065] +* Deprecated methods on `ResponseBuilder`: `if_true`, `if_some`. [#2148] +* `ResponseBuilder::json`. [#2148] +* `ResponseBuilder::{set_header, header}`. [#2148] +* `impl From for Body`. [#2148] +* `Response::build_from`. [#2159] +* Most of the status code builders on `Response`. [#2159] [#2065]: https://github.com/actix/actix-web/pull/2065 [#2148]: https://github.com/actix/actix-web/pull/2148 @@ -243,16 +243,16 @@ ## 3.0.0-beta.5 - 2021-04-02 ### Added -- `client::Connector::handshake_timeout` method for customizing TLS connection handshake timeout. [#2081] -- `client::ConnectorService` as `client::Connector::finish` method's return type [#2081] -- `client::ConnectionIo` trait alias [#2081] +* `client::Connector::handshake_timeout` method for customizing TLS connection handshake timeout. [#2081] +* `client::ConnectorService` as `client::Connector::finish` method's return type [#2081] +* `client::ConnectionIo` trait alias [#2081] ### Changed -- `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063] +* `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063] ### Removed -- Common typed HTTP headers were moved to actix-web. [2094] -- `ResponseError` impl for `actix_utils::timeout::TimeoutError`. [#2127] +* Common typed HTTP headers were moved to actix-web. [2094] +* `ResponseError` impl for `actix_utils::timeout::TimeoutError`. [#2127] [#2063]: https://github.com/actix/actix-web/pull/2063 [#2081]: https://github.com/actix/actix-web/pull/2081 @@ -262,13 +262,13 @@ ## 3.0.0-beta.4 - 2021-03-08 ### Changed -- Feature `cookies` is now optional and disabled by default. [#1981] -- `ws::hash_key` now returns array. [#2035] -- `ResponseBuilder::json` now takes `impl Serialize`. [#2052] +* Feature `cookies` is now optional and disabled by default. [#1981] +* `ws::hash_key` now returns array. [#2035] +* `ResponseBuilder::json` now takes `impl Serialize`. [#2052] ### Removed -- Re-export of `futures_channel::oneshot::Canceled` is removed from `error` mod. [#1994] -- `ResponseError` impl for `futures_channel::oneshot::Canceled` is removed. [#1994] +* Re-export of `futures_channel::oneshot::Canceled` is removed from `error` mod. [#1994] +* `ResponseError` impl for `futures_channel::oneshot::Canceled` is removed. [#1994] [#1981]: https://github.com/actix/actix-web/pull/1981 [#1994]: https://github.com/actix/actix-web/pull/1994 @@ -277,48 +277,48 @@ ## 3.0.0-beta.3 - 2021-02-10 -- No notable changes. +* No notable changes. ## 3.0.0-beta.2 - 2021-02-10 ### Added -- `TryIntoHeaderPair` trait that allows using typed and untyped headers in the same methods. [#1869] -- `ResponseBuilder::insert_header` method which allows using typed headers. [#1869] -- `ResponseBuilder::append_header` method which allows using typed headers. [#1869] -- `TestRequest::insert_header` method which allows using typed headers. [#1869] -- `ContentEncoding` implements all necessary header traits. [#1912] -- `HeaderMap::len_keys` has the behavior of the old `len` method. [#1964] -- `HeaderMap::drain` as an efficient draining iterator. [#1964] -- Implement `IntoIterator` for owned `HeaderMap`. [#1964] -- `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969] +* `TryIntoHeaderPair` trait that allows using typed and untyped headers in the same methods. [#1869] +* `ResponseBuilder::insert_header` method which allows using typed headers. [#1869] +* `ResponseBuilder::append_header` method which allows using typed headers. [#1869] +* `TestRequest::insert_header` method which allows using typed headers. [#1869] +* `ContentEncoding` implements all necessary header traits. [#1912] +* `HeaderMap::len_keys` has the behavior of the old `len` method. [#1964] +* `HeaderMap::drain` as an efficient draining iterator. [#1964] +* Implement `IntoIterator` for owned `HeaderMap`. [#1964] +* `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969] ### Changed -- `ResponseBuilder::content_type` now takes an `impl TryIntoHeaderValue` to support using typed +* `ResponseBuilder::content_type` now takes an `impl TryIntoHeaderValue` to support using typed `mime` types. [#1894] -- Renamed `TryIntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std +* Renamed `TryIntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std `TryInto` trait. [#1894] -- `Extensions::insert` returns Option of replaced item. [#1904] -- Remove `HttpResponseBuilder::json2()`. [#1903] -- Enable `HttpResponseBuilder::json()` to receive data by value and reference. [#1903] -- `client::error::ConnectError` Resolver variant contains `Box` type. [#1905] -- `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905] -- Simplify `BlockingError` type to a unit struct. It's now only triggered when blocking thread pool +* `Extensions::insert` returns Option of replaced item. [#1904] +* Remove `HttpResponseBuilder::json2()`. [#1903] +* Enable `HttpResponseBuilder::json()` to receive data by value and reference. [#1903] +* `client::error::ConnectError` Resolver variant contains `Box` type. [#1905] +* `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905] +* Simplify `BlockingError` type to a unit struct. It's now only triggered when blocking thread pool is dead. [#1957] -- `HeaderMap::len` now returns number of values instead of number of keys. [#1964] -- `HeaderMap::insert` now returns iterator of removed values. [#1964] -- `HeaderMap::remove` now returns iterator of removed values. [#1964] +* `HeaderMap::len` now returns number of values instead of number of keys. [#1964] +* `HeaderMap::insert` now returns iterator of removed values. [#1964] +* `HeaderMap::remove` now returns iterator of removed values. [#1964] ### Removed -- `ResponseBuilder::set`; use `ResponseBuilder::insert_header`. [#1869] -- `ResponseBuilder::set_header`; use `ResponseBuilder::insert_header`. [#1869] -- `ResponseBuilder::header`; use `ResponseBuilder::append_header`. [#1869] -- `TestRequest::with_hdr`; use `TestRequest::default().insert_header()`. [#1869] -- `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869] -- `actors` optional feature. [#1969] -- `ResponseError` impl for `actix::MailboxError`. [#1969] +* `ResponseBuilder::set`; use `ResponseBuilder::insert_header`. [#1869] +* `ResponseBuilder::set_header`; use `ResponseBuilder::insert_header`. [#1869] +* `ResponseBuilder::header`; use `ResponseBuilder::append_header`. [#1869] +* `TestRequest::with_hdr`; use `TestRequest::default().insert_header()`. [#1869] +* `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869] +* `actors` optional feature. [#1969] +* `ResponseError` impl for `actix::MailboxError`. [#1969] ### Documentation -- Vastly improve docs and add examples for `HeaderMap`. [#1964] +* Vastly improve docs and add examples for `HeaderMap`. [#1964] [#1869]: https://github.com/actix/actix-web/pull/1869 [#1894]: https://github.com/actix/actix-web/pull/1894 @@ -333,24 +333,24 @@ ## 3.0.0-beta.1 - 2021-01-07 ### Added -- Add `Http3` to `Protocol` enum for future compatibility and also mark `#[non_exhaustive]`. +* Add `Http3` to `Protocol` enum for future compatibility and also mark `#[non_exhaustive]`. ### Changed -- Update `actix-*` dependencies to tokio `1.0` based versions. [#1813] -- Bumped `rand` to `0.8`. -- Update `bytes` to `1.0`. [#1813] -- Update `h2` to `0.3`. [#1813] -- The `ws::Message::Text` enum variant now contains a `bytestring::ByteString`. [#1864] +* Update `actix-*` dependencies to tokio `1.0` based versions. [#1813] +* Bumped `rand` to `0.8`. +* Update `bytes` to `1.0`. [#1813] +* Update `h2` to `0.3`. [#1813] +* The `ws::Message::Text` enum variant now contains a `bytestring::ByteString`. [#1864] ### Removed -- Deprecated `on_connect` methods have been removed. Prefer the new +* Deprecated `on_connect` methods have been removed. Prefer the new `on_connect_ext` technique. [#1857] -- Remove `ResponseError` impl for `actix::actors::resolver::ResolverError` +* Remove `ResponseError` impl for `actix::actors::resolver::ResolverError` due to deprecate of resolver actor. [#1813] -- Remove `ConnectError::SslHandshakeError` and re-export of `HandshakeError`. +* Remove `ConnectError::SslHandshakeError` and re-export of `HandshakeError`. due to the removal of this type from `tokio-openssl` crate. openssl handshake error would return as `ConnectError::SslError`. [#1813] -- Remove `actix-threadpool` dependency. Use `actix_rt::task::spawn_blocking`. +* Remove `actix-threadpool` dependency. Use `actix_rt::task::spawn_blocking`. Due to this change `actix_threadpool::BlockingError` type is moved into `actix_http::error` module. [#1878] @@ -362,20 +362,20 @@ ## 2.2.1 - 2021-08-09 ### Fixed -- Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977) +* Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977) ## 2.2.0 - 2020-11-25 ### Added -- HttpResponse builders for 1xx status codes. [#1768] -- `Accept::mime_precedence` and `Accept::mime_preference`. [#1793] -- `TryFrom` and `TryFrom` for `http::header::Quality`. [#1797] +* HttpResponse builders for 1xx status codes. [#1768] +* `Accept::mime_precedence` and `Accept::mime_preference`. [#1793] +* `TryFrom` and `TryFrom` for `http::header::Quality`. [#1797] ### Fixed -- Started dropping `transfer-encoding: chunked` and `Content-Length` for 1XX and 204 responses. [#1767] +* Started dropping `transfer-encoding: chunked` and `Content-Length` for 1XX and 204 responses. [#1767] ### Changed -- Upgrade `serde_urlencoded` to `0.7`. [#1773] +* Upgrade `serde_urlencoded` to `0.7`. [#1773] [#1773]: https://github.com/actix/actix-web/pull/1773 [#1767]: https://github.com/actix/actix-web/pull/1767 @@ -386,12 +386,12 @@ ## 2.1.0 - 2020-10-30 ### Added -- Added more flexible `on_connect_ext` methods for on-connect handling. [#1754] +* Added more flexible `on_connect_ext` methods for on-connect handling. [#1754] ### Changed -- Upgrade `base64` to `0.13`. [#1744] -- Upgrade `pin-project` to `1.0`. [#1733] -- Deprecate `ResponseBuilder::{if_some, if_true}`. [#1760] +* Upgrade `base64` to `0.13`. [#1744] +* Upgrade `pin-project` to `1.0`. [#1733] +* Deprecate `ResponseBuilder::{if_some, if_true}`. [#1760] [#1760]: https://github.com/actix/actix-web/pull/1760 [#1754]: https://github.com/actix/actix-web/pull/1754 @@ -400,28 +400,28 @@ ## 2.0.0 - 2020-09-11 -- No significant changes from `2.0.0-beta.4`. +* No significant changes from `2.0.0-beta.4`. ## 2.0.0-beta.4 - 2020-09-09 ### Changed -- Update actix-codec and actix-utils dependencies. -- Update actix-connect and actix-tls dependencies. +* Update actix-codec and actix-utils dependencies. +* Update actix-connect and actix-tls dependencies. ## 2.0.0-beta.3 - 2020-08-14 ### Fixed -- Memory leak of `client::pool::ConnectorPoolSupport`. [#1626] +* Memory leak of `client::pool::ConnectorPoolSupport`. [#1626] [#1626]: https://github.com/actix/actix-web/pull/1626 ## 2.0.0-beta.2 - 2020-07-21 ### Fixed -- Potential UB in h1 decoder using uninitialized memory. [#1614] +* Potential UB in h1 decoder using uninitialized memory. [#1614] ### Changed -- Fix illegal chunked encoding. [#1615] +* Fix illegal chunked encoding. [#1615] [#1614]: https://github.com/actix/actix-web/pull/1614 [#1615]: https://github.com/actix/actix-web/pull/1615 @@ -429,10 +429,10 @@ ## 2.0.0-beta.1 - 2020-07-11 ### Changed -- Migrate cookie handling to `cookie` crate. [#1558] -- Update `sha-1` to 0.9. [#1586] -- Fix leak in client pool. [#1580] -- MSRV is now 1.41.1. +* Migrate cookie handling to `cookie` crate. [#1558] +* Update `sha-1` to 0.9. [#1586] +* Fix leak in client pool. [#1580] +* MSRV is now 1.41.1. [#1558]: https://github.com/actix/actix-web/pull/1558 [#1586]: https://github.com/actix/actix-web/pull/1586 @@ -441,15 +441,15 @@ ## 2.0.0-alpha.4 - 2020-05-21 ### Changed -- Bump minimum supported Rust version to 1.40 -- content_length function is removed, and you can set Content-Length by calling +* Bump minimum supported Rust version to 1.40 +* content_length function is removed, and you can set Content-Length by calling no_chunking function [#1439] -- `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a +* `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a `u64` instead of a `usize`. -- Update `base64` dependency to 0.12 +* Update `base64` dependency to 0.12 ### Fixed -- Support parsing of `SameSite=None` [#1503] +* Support parsing of `SameSite=None` [#1503] [#1439]: https://github.com/actix/actix-web/pull/1439 [#1503]: https://github.com/actix/actix-web/pull/1503 @@ -457,13 +457,13 @@ ## 2.0.0-alpha.3 - 2020-05-08 ### Fixed -- Correct spelling of ConnectError::Unresolved [#1487] -- Fix a mistake in the encoding of websocket continuation messages wherein +* Correct spelling of ConnectError::Unresolved [#1487] +* Fix a mistake in the encoding of websocket continuation messages wherein Item::FirstText and Item::FirstBinary are each encoded as the other. ### Changed -- Implement `std::error::Error` for our custom errors [#1422] -- Remove `failure` support for `ResponseError` since that crate +* Implement `std::error::Error` for our custom errors [#1422] +* Remove `failure` support for `ResponseError` since that crate will be deprecated in the near future. [#1422]: https://github.com/actix/actix-web/pull/1422 @@ -472,12 +472,12 @@ ## 2.0.0-alpha.2 - 2020-03-07 ### Changed -- Update `actix-connect` and `actix-tls` dependency to 2.0.0-alpha.1. [#1395] -- Change default initial window size and connection window size for HTTP2 to 2MB and 1MB +* Update `actix-connect` and `actix-tls` dependency to 2.0.0-alpha.1. [#1395] +* Change default initial window size and connection window size for HTTP2 to 2MB and 1MB respectively to improve download speed for awc when downloading large objects. [#1394] -- client::Connector accepts initial_window_size and initial_connection_window_size +* client::Connector accepts initial_window_size and initial_connection_window_size HTTP2 configuration. [#1394] -- client::Connector allowing to set max_http_version to limit HTTP version to be used. [#1394] +* client::Connector allowing to set max_http_version to limit HTTP version to be used. [#1394] [#1394]: https://github.com/actix/actix-web/pull/1394 [#1395]: https://github.com/actix/actix-web/pull/1395 @@ -485,61 +485,61 @@ ## 2.0.0-alpha.1 - 2020-02-27 ### Changed -- Update the `time` dependency to 0.2.7. -- Moved actors messages support from actix crate, enabled with feature `actors`. -- Breaking change: trait MessageBody requires Unpin and accepting `Pin<&mut Self>` instead of +* Update the `time` dependency to 0.2.7. +* Moved actors messages support from actix crate, enabled with feature `actors`. +* Breaking change: trait MessageBody requires Unpin and accepting `Pin<&mut Self>` instead of `&mut self` in the poll_next(). -- MessageBody is not implemented for &'static [u8] anymore. +* MessageBody is not implemented for &'static [u8] anymore. ### Fixed -- Allow `SameSite=None` cookies to be sent in a response. +* Allow `SameSite=None` cookies to be sent in a response. ## 1.0.1 - 2019-12-20 ### Fixed -- Poll upgrade service's readiness from HTTP service handlers -- Replace brotli with brotli2 #1224 +* Poll upgrade service's readiness from HTTP service handlers +* Replace brotli with brotli2 #1224 ## 1.0.0 - 2019-12-13 ### Added -- Add websockets continuation frame support +* Add websockets continuation frame support ### Changed -- Replace `flate2-xxx` features with `compress` +* Replace `flate2-xxx` features with `compress` ## 1.0.0-alpha.5 - 2019-12-09 ### Fixed -- Check `Upgrade` service readiness before calling it -- Fix buffer remaining capacity calculation +* Check `Upgrade` service readiness before calling it +* Fix buffer remaining capacity calculation ### Changed -- Websockets: Ping and Pong should have binary data #1049 +* Websockets: Ping and Pong should have binary data #1049 ## 1.0.0-alpha.4 - 2019-12-08 ### Added -- Add impl ResponseBuilder for Error +* Add impl ResponseBuilder for Error ### Changed -- Use rust based brotli compression library +* Use rust based brotli compression library ## 1.0.0-alpha.3 - 2019-12-07 ### Changed -- Migrate to tokio 0.2 -- Migrate to `std::future` +* Migrate to tokio 0.2 +* Migrate to `std::future` ## 0.2.11 - 2019-11-06 ### Added -- Add support for serde_json::Value to be passed as argument to ResponseBuilder.body() -- Add an additional `filename*` param in the `Content-Disposition` header of +* Add support for serde_json::Value to be passed as argument to ResponseBuilder.body() +* Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151) -- Allow to use `std::convert::Infallible` as `actix_http::error::Error` +* Allow to use `std::convert::Infallible` as `actix_http::error::Error` ### Fixed -- To be compatible with non-English error responses, `ResponseError` rendered with `text/plain; +* To be compatible with non-English error responses, `ResponseError` rendered with `text/plain; charset=utf-8` header [#1118] [#1878]: https://github.com/actix/actix-web/pull/1878 @@ -547,169 +547,169 @@ ## 0.2.10 - 2019-09-11 ### Added -- Add support for sending HTTP requests with `Rc` in addition to sending HTTP requests +* Add support for sending HTTP requests with `Rc` in addition to sending HTTP requests with `RequestHead` ### Fixed -- h2 will use error response #1080 -- on_connect result isn't added to request extensions for http2 requests #1009 +* h2 will use error response #1080 +* on_connect result isn't added to request extensions for http2 requests #1009 ## 0.2.9 - 2019-08-13 ### Changed -- Dropped the `byteorder`-dependency in favor of `stdlib`-implementation -- Update percent-encoding to 2.1 -- Update serde_urlencoded to 0.6.1 +* Dropped the `byteorder`-dependency in favor of `stdlib`-implementation +* Update percent-encoding to 2.1 +* Update serde_urlencoded to 0.6.1 ### Fixed -- Fixed a panic in the HTTP2 handshake in client HTTP requests (#1031) +* Fixed a panic in the HTTP2 handshake in client HTTP requests (#1031) ## 0.2.8 - 2019-08-01 ### Added -- Add `rustls` support -- Add `Clone` impl for `HeaderMap` +* Add `rustls` support +* Add `Clone` impl for `HeaderMap` ### Fixed -- awc client panic #1016 -- Invalid response with compression middleware enabled, but compression-related features +* awc client panic #1016 +* Invalid response with compression middleware enabled, but compression-related features disabled #997 ## 0.2.7 - 2019-07-18 ### Added -- Add support for downcasting response errors #986 +* Add support for downcasting response errors #986 ## 0.2.6 - 2019-07-17 ### Changed -- Replace `ClonableService` with local copy -- Upgrade `rand` dependency version to 0.7 +* Replace `ClonableService` with local copy +* Upgrade `rand` dependency version to 0.7 ## 0.2.5 - 2019-06-28 ### Added -- Add `on-connect` callback, `HttpServiceBuilder::on_connect()` #946 +* Add `on-connect` callback, `HttpServiceBuilder::on_connect()` #946 ### Changed -- Use `encoding_rs` crate instead of unmaintained `encoding` crate -- Add `Copy` and `Clone` impls for `ws::Codec` +* Use `encoding_rs` crate instead of unmaintained `encoding` crate +* Add `Copy` and `Clone` impls for `ws::Codec` ## 0.2.4 - 2019-06-16 ### Fixed -- Do not compress NoContent (204) responses #918 +* Do not compress NoContent (204) responses #918 ## 0.2.3 - 2019-06-02 ### Added -- Debug impl for ResponseBuilder -- From SizedStream and BodyStream for Body +* Debug impl for ResponseBuilder +* From SizedStream and BodyStream for Body ### Changed -- SizedStream uses u64 +* SizedStream uses u64 ## 0.2.2 - 2019-05-29 ### Fixed -- Parse incoming stream before closing stream on disconnect #868 +* Parse incoming stream before closing stream on disconnect #868 ## 0.2.1 - 2019-05-25 ### Fixed -- Handle socket read disconnect +* Handle socket read disconnect ## 0.2.0 - 2019-05-12 ### Changed -- Update actix-service to 0.4 -- Expect and upgrade services accept `ServerConfig` config. +* Update actix-service to 0.4 +* Expect and upgrade services accept `ServerConfig` config. ### Deleted -- `OneRequest` service +* `OneRequest` service ## 0.1.5 - 2019-05-04 ### Fixed -- Clean up response extensions in response pool #817 +* Clean up response extensions in response pool #817 ## 0.1.4 - 2019-04-24 ### Added -- Allow to render h1 request headers in `Camel-Case` +* Allow to render h1 request headers in `Camel-Case` ### Fixed -- Read until eof for http/1.0 responses #771 +* Read until eof for http/1.0 responses #771 ## 0.1.3 - 2019-04-23 ### Fixed -- Fix http client pool management -- Fix http client wait queue management #794 +* Fix http client pool management +* Fix http client wait queue management #794 ## 0.1.2 - 2019-04-23 ### Fixed -- Fix BorrowMutError panic in client connector #793 +* Fix BorrowMutError panic in client connector #793 ## 0.1.1 - 2019-04-19 ### Changed -- Cookie::max_age() accepts value in seconds -- Cookie::max_age_time() accepts value in time::Duration -- Allow to specify server address for client connector +* Cookie::max_age() accepts value in seconds +* Cookie::max_age_time() accepts value in time::Duration +* Allow to specify server address for client connector ## 0.1.0 - 2019-04-16 ### Added -- Expose peer addr via `Request::peer_addr()` and `RequestHead::peer_addr` +* Expose peer addr via `Request::peer_addr()` and `RequestHead::peer_addr` ### Changed -- `actix_http::encoding` always available -- use trust-dns-resolver 0.11.0 +* `actix_http::encoding` always available +* use trust-dns-resolver 0.11.0 ## 0.1.0-alpha.5 - 2019-04-12 ### Added -- Allow to use custom service for upgrade requests -- Added `h1::SendResponse` future. +* Allow to use custom service for upgrade requests +* Added `h1::SendResponse` future. ### Changed -- MessageBody::length() renamed to MessageBody::size() for consistency -- ws handshake verification functions take RequestHead instead of Request +* MessageBody::length() renamed to MessageBody::size() for consistency +* ws handshake verification functions take RequestHead instead of Request ## 0.1.0-alpha.4 - 2019-04-08 ### Added -- Allow to use custom `Expect` handler -- Add minimal `std::error::Error` impl for `Error` +* Allow to use custom `Expect` handler +* Add minimal `std::error::Error` impl for `Error` ### Changed -- Export IntoHeaderValue -- Render error and return as response body -- Use thread pool for response body compression +* Export IntoHeaderValue +* Render error and return as response body +* Use thread pool for response body compression ### Deleted -- Removed PayloadBuffer +* Removed PayloadBuffer ## 0.1.0-alpha.3 - 2019-04-02 ### Added -- Warn when an unsealed private cookie isn't valid UTF-8 +* Warn when an unsealed private cookie isn't valid UTF-8 ### Fixed -- Rust 1.31.0 compatibility -- Preallocate read buffer for h1 codec -- Detect socket disconnection during protocol selection +* Rust 1.31.0 compatibility +* Preallocate read buffer for h1 codec +* Detect socket disconnection during protocol selection ## 0.1.0-alpha.2 - 2019-03-29 ### Added -- Added ws::Message::Nop, no-op websockets message +* Added ws::Message::Nop, no-op websockets message ### Changed -- Do not use thread pool for decompression if chunk size is smaller than 2048. +* Do not use thread pool for decompression if chunk size is smaller than 2048. ## 0.1.0-alpha.1 - 2019-03-28 -- Initial impl +* Initial impl diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index e58c3ee24..8d9c1640f 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -4,119 +4,119 @@ ## 0.4.0-beta.10 - 2021-12-11 -- No significant changes since `0.4.0-beta.9`. +* No significant changes since `0.4.0-beta.9`. ## 0.4.0-beta.9 - 2021-12-01 -- Polling `Field` after dropping `Multipart` now fails immediately instead of hanging forever. [#2463] +* Polling `Field` after dropping `Multipart` now fails immediately instead of hanging forever. [#2463] [#2463]: https://github.com/actix/actix-web/pull/2463 ## 0.4.0-beta.8 - 2021-11-22 -- Ensure a correct Content-Disposition header is included in every part of a multipart message. [#2451] -- Added `MultipartError::NoContentDisposition` variant. [#2451] -- Since Content-Disposition is now ensured, `Field::content_disposition` is now infallible. [#2451] -- Added `Field::name` method for getting the field name. [#2451] -- `MultipartError` now marks variants with inner errors as the source. [#2451] -- `MultipartError` is now marked as non-exhaustive. [#2451] +* Ensure a correct Content-Disposition header is included in every part of a multipart message. [#2451] +* Added `MultipartError::NoContentDisposition` variant. [#2451] +* Since Content-Disposition is now ensured, `Field::content_disposition` is now infallible. [#2451] +* Added `Field::name` method for getting the field name. [#2451] +* `MultipartError` now marks variants with inner errors as the source. [#2451] +* `MultipartError` is now marked as non-exhaustive. [#2451] [#2451]: https://github.com/actix/actix-web/pull/2451 ## 0.4.0-beta.7 - 2021-10-20 -- Minimum supported Rust version (MSRV) is now 1.52. +* Minimum supported Rust version (MSRV) is now 1.52. ## 0.4.0-beta.6 - 2021-09-09 -- Minimum supported Rust version (MSRV) is now 1.51. +* Minimum supported Rust version (MSRV) is now 1.51. ## 0.4.0-beta.5 - 2021-06-17 -- No notable changes. +* No notable changes. ## 0.4.0-beta.4 - 2021-04-02 -- No notable changes. +* No notable changes. ## 0.4.0-beta.3 - 2021-03-09 -- No notable changes. +* No notable changes. ## 0.4.0-beta.2 - 2021-02-10 -- No notable changes. +* No notable changes. ## 0.4.0-beta.1 - 2021-01-07 -- Fix multipart consuming payload before header checks. [#1513] -- Update `bytes` to `1.0`. [#1813] +* Fix multipart consuming payload before header checks. [#1513] +* Update `bytes` to `1.0`. [#1813] [#1813]: https://github.com/actix/actix-web/pull/1813 [#1513]: https://github.com/actix/actix-web/pull/1513 ## 0.3.0 - 2020-09-11 -- No significant changes from `0.3.0-beta.2`. +* No significant changes from `0.3.0-beta.2`. ## 0.3.0-beta.2 - 2020-09-10 -- Update `actix-*` dependencies to latest versions. +* Update `actix-*` dependencies to latest versions. ## 0.3.0-beta.1 - 2020-07-15 -- Update `actix-web` to 3.0.0-beta.1 +* Update `actix-web` to 3.0.0-beta.1 ## 0.3.0-alpha.1 - 2020-05-25 -- Update `actix-web` to 3.0.0-alpha.3 -- Bump minimum supported Rust version to 1.40 -- Minimize `futures` dependencies -- Remove the unused `time` dependency -- Fix missing `std::error::Error` implement for `MultipartError`. +* Update `actix-web` to 3.0.0-alpha.3 +* Bump minimum supported Rust version to 1.40 +* Minimize `futures` dependencies +* Remove the unused `time` dependency +* Fix missing `std::error::Error` implement for `MultipartError`. ## [0.2.0] - 2019-12-20 -- Release +* Release ## [0.2.0-alpha.4] - 2019-12-xx -- Multipart handling now handles Pending during read of boundary #1205 +* Multipart handling now handles Pending during read of boundary #1205 ## [0.2.0-alpha.2] - 2019-12-03 -- Migrate to `std::future` +* Migrate to `std::future` ## [0.1.4] - 2019-09-12 -- Multipart handling now parses requests which do not end in CRLF #1038 +* Multipart handling now parses requests which do not end in CRLF #1038 ## [0.1.3] - 2019-08-18 -- Fix ring dependency from actix-web default features for #741. +* Fix ring dependency from actix-web default features for #741. ## [0.1.2] - 2019-06-02 -- Fix boundary parsing #876 +* Fix boundary parsing #876 ## [0.1.1] - 2019-05-25 -- Fix disconnect handling #834 +* Fix disconnect handling #834 ## [0.1.0] - 2019-05-18 -- Release +* Release ## [0.1.0-beta.4] - 2019-05-12 -- Handle cancellation of uploads #736 +* Handle cancellation of uploads #736 -- Upgrade to actix-web 1.0.0-beta.4 +* Upgrade to actix-web 1.0.0-beta.4 ## [0.1.0-beta.1] - 2019-04-21 -- Do not support nested multipart +* Do not support nested multipart -- Split multipart support to separate crate +* Split multipart support to separate crate -- Optimize multipart handling #634, #769 +* Optimize multipart handling #634, #769 diff --git a/actix-router/CHANGES.md b/actix-router/CHANGES.md index 0a6a56359..d0ed55c88 100644 --- a/actix-router/CHANGES.md +++ b/actix-router/CHANGES.md @@ -4,20 +4,20 @@ ## 0.5.0-beta.3 - 2021-12-17 -- Minimum supported Rust version (MSRV) is now 1.52. +* Minimum supported Rust version (MSRV) is now 1.52. ## 0.5.0-beta.2 - 2021-09-09 -- Introduce `ResourceDef::join`. [#380] -- Disallow prefix routes with tail segments. [#379] -- Enforce path separators on dynamic prefixes. [#378] -- Improve malformed path error message. [#384] -- Prefix segments now always end with with a segment delimiter or end-of-input. [#2355] -- Prefix segments with trailing slashes define a trailing empty segment. [#2355] -- Support multi-pattern prefixes and joins. [#2356] -- `ResourceDef::pattern` now returns the first pattern in multi-pattern resources. [#2356] -- Support `build_resource_path` on multi-pattern resources. [#2356] -- Minimum supported Rust version (MSRV) is now 1.51. +* Introduce `ResourceDef::join`. [#380] +* Disallow prefix routes with tail segments. [#379] +* Enforce path separators on dynamic prefixes. [#378] +* Improve malformed path error message. [#384] +* Prefix segments now always end with with a segment delimiter or end-of-input. [#2355] +* Prefix segments with trailing slashes define a trailing empty segment. [#2355] +* Support multi-pattern prefixes and joins. [#2356] +* `ResourceDef::pattern` now returns the first pattern in multi-pattern resources. [#2356] +* Support `build_resource_path` on multi-pattern resources. [#2356] +* Minimum supported Rust version (MSRV) is now 1.51. [#378]: https://github.com/actix/actix-net/pull/378 [#379]: https://github.com/actix/actix-net/pull/379 @@ -28,23 +28,23 @@ ## 0.5.0-beta.1 - 2021-07-20 -- Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366] -- Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373] -- Fix segment interpolation leaving `Path` in unintended state after matching. [#368] -- Fix `ResourceDef` `PartialEq` implementation. [#373] -- Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372] -- Implement `IntoPatterns` for `bytestring::ByteString`. [#372] -- Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370] -- Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371] -- `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373] -- Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371] -- Rename `ResourceDef::{is_prefix_match => find_match}`. [#373] -- Rename `ResourceDef::{match_path => capture_match_info}`. [#373] -- Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373] -- Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373] -- Rename `Router::{*_checked => *_fn}`. [#373] -- Return type of `ResourceDef::name` is now `Option<&str>`. [#373] -- Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373] +* Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366] +* Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373] +* Fix segment interpolation leaving `Path` in unintended state after matching. [#368] +* Fix `ResourceDef` `PartialEq` implementation. [#373] +* Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372] +* Implement `IntoPatterns` for `bytestring::ByteString`. [#372] +* Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370] +* Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371] +* `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373] +* Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371] +* Rename `ResourceDef::{is_prefix_match => find_match}`. [#373] +* Rename `ResourceDef::{match_path => capture_match_info}`. [#373] +* Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373] +* Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373] +* Rename `Router::{*_checked => *_fn}`. [#373] +* Return type of `ResourceDef::name` is now `Option<&str>`. [#373] +* Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373] [#368]: https://github.com/actix/actix-net/pull/368 [#366]: https://github.com/actix/actix-net/pull/366 @@ -56,10 +56,10 @@ ## 0.4.0 - 2021-06-06 -- When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357] -- Path tail patterns now match new lines (`\n`) in request URL. [#360] -- Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359] -- Methods `Path::{add, add_static}` now take `impl Into>`. [#345] +* When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357] +* Path tail patterns now match new lines (`\n`) in request URL. [#360] +* Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359] +* Methods `Path::{add, add_static}` now take `impl Into>`. [#345] [#345]: https://github.com/actix/actix-net/pull/345 [#357]: https://github.com/actix/actix-net/pull/357 @@ -68,68 +68,68 @@ ## 0.3.0 - 2019-12-31 -- Version was yanked previously. See https://crates.io/crates/actix-router/0.3.0 +* Version was yanked previously. See https://crates.io/crates/actix-router/0.3.0 ## 0.2.7 - 2021-02-06 -- Add `Router::recognize_checked` [#247] +* Add `Router::recognize_checked` [#247] [#247]: https://github.com/actix/actix-net/pull/247 ## 0.2.6 - 2021-01-09 -- Use `bytestring` version range compatible with Bytes v1.0. [#246] +* Use `bytestring` version range compatible with Bytes v1.0. [#246] [#246]: https://github.com/actix/actix-net/pull/246 ## 0.2.5 - 2020-09-20 -- Fix `from_hex()` method +* Fix `from_hex()` method ## 0.2.4 - 2019-12-31 -- Add `ResourceDef::resource_path_named()` path generation method +* Add `ResourceDef::resource_path_named()` path generation method ## 0.2.3 - 2019-12-25 -- Add impl `IntoPattern` for `&String` +* Add impl `IntoPattern` for `&String` ## 0.2.2 - 2019-12-25 -- Use `IntoPattern` for `RouterBuilder::path()` +* Use `IntoPattern` for `RouterBuilder::path()` ## 0.2.1 - 2019-12-25 -- Add `IntoPattern` trait -- Add multi-pattern resources +* Add `IntoPattern` trait +* Add multi-pattern resources ## 0.2.0 - 2019-12-07 -- Update http to 0.2 -- Update regex to 1.3 -- Use bytestring instead of string +* Update http to 0.2 +* Update regex to 1.3 +* Use bytestring instead of string ## 0.1.5 - 2019-05-15 -- Remove debug prints +* Remove debug prints ## 0.1.4 - 2019-05-15 -- Fix checked resource match +* Fix checked resource match ## 0.1.3 - 2019-04-22 -- Added support for `remainder match` (i.e "/path/{tail}*") +* Added support for `remainder match` (i.e "/path/{tail}*") ## 0.1.2 - 2019-04-07 -- Export `Quoter` type -- Allow to reset `Path` instance +* Export `Quoter` type +* Allow to reset `Path` instance ## 0.1.1 - 2019-04-03 -- Get dynamic segment by name instead of iterator. +* Get dynamic segment by name instead of iterator. ## 0.1.0 - 2019-03-09 -- Initial release +* Initial release diff --git a/actix-test/CHANGES.md b/actix-test/CHANGES.md index e3deeb3f4..ef78ac54a 100644 --- a/actix-test/CHANGES.md +++ b/actix-test/CHANGES.md @@ -4,46 +4,46 @@ ## 0.1.0-beta.9 - 2021-12-17 -- Re-export `actix_http::body::to_bytes`. [#2518] -- Update `actix_web::test` re-exports. [#2518] +* Re-export `actix_http::body::to_bytes`. [#2518] +* Update `actix_web::test` re-exports. [#2518] [#2518]: https://github.com/actix/actix-web/pull/2518 ## 0.1.0-beta.8 - 2021-12-11 -- No significant changes since `0.1.0-beta.7`. +* No significant changes since `0.1.0-beta.7`. ## 0.1.0-beta.7 - 2021-11-22 -- Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408] +* Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408] [#2408]: https://github.com/actix/actix-web/pull/2408 ## 0.1.0-beta.6 - 2021-11-15 -- No significant changes from `0.1.0-beta.5`. +* No significant changes from `0.1.0-beta.5`. ## 0.1.0-beta.5 - 2021-10-20 -- Updated rustls to v0.20. [#2414] -- Minimum supported Rust version (MSRV) is now 1.52. +* Updated rustls to v0.20. [#2414] +* Minimum supported Rust version (MSRV) is now 1.52. [#2414]: https://github.com/actix/actix-web/pull/2414 ## 0.1.0-beta.4 - 2021-09-09 -- Minimum supported Rust version (MSRV) is now 1.51. +* Minimum supported Rust version (MSRV) is now 1.51. ## 0.1.0-beta.3 - 2021-06-20 -- No significant changes from `0.1.0-beta.2`. +* No significant changes from `0.1.0-beta.2`. ## 0.1.0-beta.2 - 2021-04-17 -- No significant changes from `0.1.0-beta.1`. +* No significant changes from `0.1.0-beta.1`. ## 0.1.0-beta.1 - 2021-04-02 -- Move integration testing structs from `actix-web`. [#2112] +* Move integration testing structs from `actix-web`. [#2112] [#2112]: https://github.com/actix/actix-web/pull/2112 diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md index 6abfe2c61..d3078499c 100644 --- a/actix-web-actors/CHANGES.md +++ b/actix-web-actors/CHANGES.md @@ -4,105 +4,105 @@ ## 4.0.0-beta.8 - 2021-12-11 -- Add `ws:WsResponseBuilder` for building WebSocket session response. [#1920] -- Deprecate `ws::{start_with_addr, start_with_protocols}`. [#1920] -- Minimum supported Rust version (MSRV) is now 1.52. +* Add `ws:WsResponseBuilder` for building WebSocket session response. [#1920] +* Deprecate `ws::{start_with_addr, start_with_protocols}`. [#1920] +* Minimum supported Rust version (MSRV) is now 1.52. [#1920]: https://github.com/actix/actix-web/pull/1920 ## 4.0.0-beta.7 - 2021-09-09 -- Minimum supported Rust version (MSRV) is now 1.51. +* Minimum supported Rust version (MSRV) is now 1.51. ## 4.0.0-beta.6 - 2021-06-26 -- Update `actix` to `0.12`. [#2277] +* Update `actix` to `0.12`. [#2277] [#2277]: https://github.com/actix/actix-web/pull/2277 ## 4.0.0-beta.5 - 2021-06-17 -- No notable changes. +* No notable changes. ## 4.0.0-beta.4 - 2021-04-02 -- No notable changes. +* No notable changes. ## 4.0.0-beta.3 - 2021-03-09 -- No notable changes. +* No notable changes. ## 4.0.0-beta.2 - 2021-02-10 -- No notable changes. +* No notable changes. ## 4.0.0-beta.1 - 2021-01-07 -- Update `pin-project` to `1.0`. -- Update `bytes` to `1.0`. [#1813] -- `WebsocketContext::text` now takes an `Into`. [#1864] +* Update `pin-project` to `1.0`. +* Update `bytes` to `1.0`. [#1813] +* `WebsocketContext::text` now takes an `Into`. [#1864] [#1813]: https://github.com/actix/actix-web/pull/1813 [#1864]: https://github.com/actix/actix-web/pull/1864 ## 3.0.0 - 2020-09-11 -- No significant changes from `3.0.0-beta.2`. +* No significant changes from `3.0.0-beta.2`. ## 3.0.0-beta.2 - 2020-09-10 -- Update `actix-*` dependencies to latest versions. +* Update `actix-*` dependencies to latest versions. ## [3.0.0-beta.1] - 2020-xx-xx -- Update `actix-web` & `actix-http` dependencies to beta.1 -- Bump minimum supported Rust version to 1.40 +* Update `actix-web` & `actix-http` dependencies to beta.1 +* Bump minimum supported Rust version to 1.40 ## [3.0.0-alpha.1] - 2020-05-08 -- Update the actix-web dependency to 3.0.0-alpha.1 -- Update the actix dependency to 0.10.0-alpha.2 -- Update the actix-http dependency to 2.0.0-alpha.3 +* Update the actix-web dependency to 3.0.0-alpha.1 +* Update the actix dependency to 0.10.0-alpha.2 +* Update the actix-http dependency to 2.0.0-alpha.3 ## [2.0.0] - 2019-12-20 -- Release +* Release ## [2.0.0-alpha.1] - 2019-12-15 -- Migrate to actix-web 2.0.0 +* Migrate to actix-web 2.0.0 ## [1.0.4] - 2019-12-07 -- Allow comma-separated websocket subprotocols without spaces (#1172) +* Allow comma-separated websocket subprotocols without spaces (#1172) ## [1.0.3] - 2019-11-14 -- Update actix-web and actix-http dependencies +* Update actix-web and actix-http dependencies ## [1.0.2] - 2019-07-20 -- Add `ws::start_with_addr()`, returning the address of the created actor, along +* Add `ws::start_with_addr()`, returning the address of the created actor, along with the `HttpResponse`. -- Add support for specifying protocols on websocket handshake #835 +* Add support for specifying protocols on websocket handshake #835 ## [1.0.1] - 2019-06-28 -- Allow to use custom ws codec with `WebsocketContext` #925 +* Allow to use custom ws codec with `WebsocketContext` #925 ## [1.0.0] - 2019-05-29 -- Update actix-http and actix-web +* Update actix-http and actix-web ## [0.1.0-alpha.3] - 2019-04-02 -- Update actix-http and actix-web +* Update actix-http and actix-web ## [0.1.0-alpha.2] - 2019-03-29 -- Update actix-http and actix-web +* Update actix-http and actix-web ## [0.1.0-alpha.1] - 2019-03-28 -- Initial impl +* Initial impl diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index 0d881d303..309274563 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -4,101 +4,101 @@ ## 0.5.0-beta.6 - 2021-12-11 -- No significant changes since `0.5.0-beta.5`. +* No significant changes since `0.5.0-beta.5`. ## 0.5.0-beta.5 - 2021-10-20 -- Improve error recovery potential when macro input is invalid. [#2410] -- Add `#[actix_web::test]` macro for setting up tests with a runtime. [#2409] -- Minimum supported Rust version (MSRV) is now 1.52. +* Improve error recovery potential when macro input is invalid. [#2410] +* Add `#[actix_web::test]` macro for setting up tests with a runtime. [#2409] +* Minimum supported Rust version (MSRV) is now 1.52. [#2410]: https://github.com/actix/actix-web/pull/2410 [#2409]: https://github.com/actix/actix-web/pull/2409 ## 0.5.0-beta.4 - 2021-09-09 -- In routing macros, paths are now validated at compile time. [#2350] -- Minimum supported Rust version (MSRV) is now 1.51. +* In routing macros, paths are now validated at compile time. [#2350] +* Minimum supported Rust version (MSRV) is now 1.51. [#2350]: https://github.com/actix/actix-web/pull/2350 ## 0.5.0-beta.3 - 2021-06-17 -- No notable changes. +* No notable changes. ## 0.5.0-beta.2 - 2021-03-09 -- Preserve doc comments when using route macros. [#2022] -- Add `name` attribute to `route` macro. [#1934] +* Preserve doc comments when using route macros. [#2022] +* Add `name` attribute to `route` macro. [#1934] [#2022]: https://github.com/actix/actix-web/pull/2022 [#1934]: https://github.com/actix/actix-web/pull/1934 ## 0.5.0-beta.1 - 2021-02-10 -- Use new call signature for `System::new`. +* Use new call signature for `System::new`. ## 0.4.0 - 2020-09-20 -- Added compile success and failure testing. [#1677] -- Add `route` macro for supporting multiple HTTP methods guards. [#1674] +* Added compile success and failure testing. [#1677] +* Add `route` macro for supporting multiple HTTP methods guards. [#1674] [#1677]: https://github.com/actix/actix-web/pull/1677 [#1674]: https://github.com/actix/actix-web/pull/1674 ## 0.3.0 - 2020-09-11 -- No significant changes from `0.3.0-beta.1`. +* No significant changes from `0.3.0-beta.1`. ## 0.3.0-beta.1 - 2020-07-14 -- Add main entry-point macro that uses re-exported runtime. [#1559] +* Add main entry-point macro that uses re-exported runtime. [#1559] [#1559]: https://github.com/actix/actix-web/pull/1559 ## 0.2.2 - 2020-05-23 -- Add resource middleware on actix-web-codegen [#1467] +* Add resource middleware on actix-web-codegen [#1467] [#1467]: https://github.com/actix/actix-web/pull/1467 ## 0.2.1 - 2020-02-25 -- Add `#[allow(missing_docs)]` attribute to generated structs [#1368] -- Allow the handler function to be named as `config` [#1290] +* Add `#[allow(missing_docs)]` attribute to generated structs [#1368] +* Allow the handler function to be named as `config` [#1290] [#1368]: https://github.com/actix/actix-web/issues/1368 [#1290]: https://github.com/actix/actix-web/issues/1290 ## 0.2.0 - 2019-12-13 -- Generate code for actix-web 2.0 +* Generate code for actix-web 2.0 ## 0.1.3 - 2019-10-14 -- Bump up `syn` & `quote` to 1.0 -- Provide better error message +* Bump up `syn` & `quote` to 1.0 +* Provide better error message ## 0.1.2 - 2019-06-04 -- Add macros for head, options, trace, connect and patch http methods +* Add macros for head, options, trace, connect and patch http methods ## 0.1.1 - 2019-06-01 -- Add syn "extra-traits" feature +* Add syn "extra-traits" feature ## 0.1.0 - 2019-05-18 -- Release +* Release ## 0.1.0-beta.1 - 2019-04-20 -- Gen code for actix-web 1.0.0-beta.1 +* Gen code for actix-web 1.0.0-beta.1 ## 0.1.0-alpha.6 - 2019-04-14 -- Gen code for actix-web 1.0.0-alpha.6 +* Gen code for actix-web 1.0.0-alpha.6 ## 0.1.0-alpha.1 - 2019-03-28 -- Initial impl +* Initial impl diff --git a/awc/CHANGES.md b/awc/CHANGES.md index b5144b7a2..7b822930c 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,75 +1,75 @@ # Changes ## Unreleased - 2021-xx-xx -- Rename `Connector::{ssl => openssl}`. [#2503] -- Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503] +* Rename `Connector::{ssl => openssl}`. [#2503] +* Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503] [#2503]: https://github.com/actix/actix-web/pull/2503 ## 3.0.0-beta.14 - 2021-12-17 -- Add `ClientBuilder::add_default_header` and deprecate `ClientBuilder::header`. [#2510] +* Add `ClientBuilder::add_default_header` and deprecate `ClientBuilder::header`. [#2510] [#2510]: https://github.com/actix/actix-web/pull/2510 ## 3.0.0-beta.13 - 2021-12-11 -- No significant changes since `3.0.0-beta.12`. +* No significant changes since `3.0.0-beta.12`. ## 3.0.0-beta.12 - 2021-11-30 -- Update `actix-tls` to `3.0.0-rc.1`. [#2474] +* Update `actix-tls` to `3.0.0-rc.1`. [#2474] [#2474]: https://github.com/actix/actix-web/pull/2474 ## 3.0.0-beta.11 - 2021-11-22 -- No significant changes from `3.0.0-beta.10`. +* No significant changes from `3.0.0-beta.10`. ## 3.0.0-beta.10 - 2021-11-15 -- No significant changes from `3.0.0-beta.9`. +* No significant changes from `3.0.0-beta.9`. ## 3.0.0-beta.9 - 2021-10-20 -- Updated rustls to v0.20. [#2414] +* Updated rustls to v0.20. [#2414] [#2414]: https://github.com/actix/actix-web/pull/2414 ## 3.0.0-beta.8 - 2021-09-09 ### Changed -- Send headers within the redirect requests. [#2310] +* Send headers within the redirect requests. [#2310] [#2310]: https://github.com/actix/actix-web/pull/2310 ## 3.0.0-beta.7 - 2021-06-26 ### Changed -- Change compression algorithm features flags. [#2250] +* Change compression algorithm features flags. [#2250] [#2250]: https://github.com/actix/actix-web/pull/2250 ## 3.0.0-beta.6 - 2021-06-17 -- No significant changes since 3.0.0-beta.5. +* No significant changes since 3.0.0-beta.5. ## 3.0.0-beta.5 - 2021-04-17 ### Removed -- Deprecated methods on `ClientRequest`: `if_true`, `if_some`. [#2148] +* Deprecated methods on `ClientRequest`: `if_true`, `if_some`. [#2148] [#2148]: https://github.com/actix/actix-web/pull/2148 ## 3.0.0-beta.4 - 2021-04-02 ### Added -- Add `Client::headers` to get default mut reference of `HeaderMap` of client object. [#2114] +* Add `Client::headers` to get default mut reference of `HeaderMap` of client object. [#2114] ### Changed -- `ConnectorService` type is renamed to `BoxConnectorService`. [#2081] -- Fix http/https encoding when enabling `compress` feature. [#2116] -- Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header +* `ConnectorService` type is renamed to `BoxConnectorService`. [#2081] +* Fix http/https encoding when enabling `compress` feature. [#2116] +* Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header methods now take `TryIntoHeaderPair` tuples. [#2094] [#2081]: https://github.com/actix/actix-web/pull/2081 @@ -80,16 +80,16 @@ ## 3.0.0-beta.3 - 2021-03-08 ### Added -- `ClientResponse::timeout` for set the timeout of collecting response body. [#1931] -- `ClientBuilder::local_address` for bind to a local ip address for this client. [#2024] +* `ClientResponse::timeout` for set the timeout of collecting response body. [#1931] +* `ClientBuilder::local_address` for bind to a local ip address for this client. [#2024] ### Changed -- Feature `cookies` is now optional and enabled by default. [#1981] -- `ClientBuilder::connector` method would take `actix_http::client::Connector` type. [#2008] -- Basic auth password now takes blank passwords as an empty string instead of Option. [#2050] +* Feature `cookies` is now optional and enabled by default. [#1981] +* `ClientBuilder::connector` method would take `actix_http::client::Connector` type. [#2008] +* Basic auth password now takes blank passwords as an empty string instead of Option. [#2050] ### Removed -- `ClientBuilder::default` function [#2008] +* `ClientBuilder::default` function [#2008] [#1931]: https://github.com/actix/actix-web/pull/1931 [#1981]: https://github.com/actix/actix-web/pull/1981 @@ -100,18 +100,18 @@ ## 3.0.0-beta.2 - 2021-02-10 ### Added -- `ClientRequest::insert_header` method which allows using typed headers. [#1869] -- `ClientRequest::append_header` method which allows using typed headers. [#1869] -- `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969] +* `ClientRequest::insert_header` method which allows using typed headers. [#1869] +* `ClientRequest::append_header` method which allows using typed headers. [#1869] +* `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969] ### Changed -- Relax default timeout for `Connector` to 5 seconds(original 1 second). [#1905] +* Relax default timeout for `Connector` to 5 seconds(original 1 second). [#1905] ### Removed -- `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869] -- `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869] -- `ClientRequest::set_header_if_none`; use `ClientRequest::insert_header_if_none`. [#1869] -- `ClientRequest::header`; use `ClientRequest::append_header`. [#1869] +* `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869] +* `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869] +* `ClientRequest::set_header_if_none`; use `ClientRequest::insert_header_if_none`. [#1869] +* `ClientRequest::header`; use `ClientRequest::append_header`. [#1869] [#1869]: https://github.com/actix/actix-web/pull/1869 [#1905]: https://github.com/actix/actix-web/pull/1905 @@ -120,32 +120,32 @@ ## 3.0.0-beta.1 - 2021-01-07 ### Changed -- Update `rand` to `0.8` -- Update `bytes` to `1.0`. [#1813] -- Update `rust-tls` to `0.19`. [#1813] +* Update `rand` to `0.8` +* Update `bytes` to `1.0`. [#1813] +* Update `rust-tls` to `0.19`. [#1813] [#1813]: https://github.com/actix/actix-web/pull/1813 ## 2.0.3 - 2020-11-29 ### Fixed -- Ensure `actix-http` dependency uses same `serde_urlencoded`. +* Ensure `actix-http` dependency uses same `serde_urlencoded`. ## 2.0.2 - 2020-11-25 ### Changed -- Upgrade `serde_urlencoded` to `0.7`. [#1773] +* Upgrade `serde_urlencoded` to `0.7`. [#1773] [#1773]: https://github.com/actix/actix-web/pull/1773 ## 2.0.1 - 2020-10-30 ### Changed -- Upgrade `base64` to `0.13`. [#1744] -- Deprecate `ClientRequest::{if_some, if_true}`. [#1760] +* Upgrade `base64` to `0.13`. [#1744] +* Deprecate `ClientRequest::{if_some, if_true}`. [#1760] ### Fixed -- Use `Accept-Encoding: identity` instead of `Accept-Encoding: br` when no compression feature +* Use `Accept-Encoding: identity` instead of `Accept-Encoding: br` when no compression feature is enabled [#1737] [#1737]: https://github.com/actix/actix-web/pull/1737 @@ -155,209 +155,209 @@ ## 2.0.0 - 2020-09-11 ### Changed -- `Client::build` was renamed to `Client::builder`. +* `Client::build` was renamed to `Client::builder`. ## 2.0.0-beta.4 - 2020-09-09 ### Changed -- Update actix-codec & actix-tls dependencies. +* Update actix-codec & actix-tls dependencies. ## 2.0.0-beta.3 - 2020-08-17 ### Changed -- Update `rustls` to 0.18 +* Update `rustls` to 0.18 ## 2.0.0-beta.2 - 2020-07-21 ### Changed -- Update `actix-http` dependency to 2.0.0-beta.2 +* Update `actix-http` dependency to 2.0.0-beta.2 ## [2.0.0-beta.1] - 2020-07-14 ### Changed -- Update `actix-http` dependency to 2.0.0-beta.1 +* Update `actix-http` dependency to 2.0.0-beta.1 ## [2.0.0-alpha.2] - 2020-05-21 ### Changed -- Implement `std::error::Error` for our custom errors [#1422] -- Bump minimum supported Rust version to 1.40 -- Update `base64` dependency to 0.12 +* Implement `std::error::Error` for our custom errors [#1422] +* Bump minimum supported Rust version to 1.40 +* Update `base64` dependency to 0.12 [#1422]: https://github.com/actix/actix-web/pull/1422 ## [2.0.0-alpha.1] - 2020-03-11 -- Update `actix-http` dependency to 2.0.0-alpha.2 -- Update `rustls` dependency to 0.17 -- ClientBuilder accepts initial_window_size and initial_connection_window_size HTTP2 configuration -- ClientBuilder allowing to set max_http_version to limit HTTP version to be used +* Update `actix-http` dependency to 2.0.0-alpha.2 +* Update `rustls` dependency to 0.17 +* ClientBuilder accepts initial_window_size and initial_connection_window_size HTTP2 configuration +* ClientBuilder allowing to set max_http_version to limit HTTP version to be used ## [1.0.1] - 2019-12-15 -- Fix compilation with default features off +* Fix compilation with default features off ## [1.0.0] - 2019-12-13 -- Release +* Release ## [1.0.0-alpha.3] -- Migrate to `std::future` +* Migrate to `std::future` ## [0.2.8] - 2019-11-06 -- Add support for setting query from Serialize type for client request. +* Add support for setting query from Serialize type for client request. ## [0.2.7] - 2019-09-25 ### Added -- Remaining getter methods for `ClientRequest`'s private `head` field #1101 +* Remaining getter methods for `ClientRequest`'s private `head` field #1101 ## [0.2.6] - 2019-09-12 ### Added -- Export frozen request related types. +* Export frozen request related types. ## [0.2.5] - 2019-09-11 ### Added -- Add `FrozenClientRequest` to support retries for sending HTTP requests +* Add `FrozenClientRequest` to support retries for sending HTTP requests ### Changed -- Ensure that the `Host` header is set when initiating a WebSocket client connection. +* Ensure that the `Host` header is set when initiating a WebSocket client connection. ## [0.2.4] - 2019-08-13 ### Changed -- Update percent-encoding to "2.1" +* Update percent-encoding to "2.1" -- Update serde_urlencoded to "0.6.1" +* Update serde_urlencoded to "0.6.1" ## [0.2.3] - 2019-08-01 ### Added -- Add `rustls` support +* Add `rustls` support ## [0.2.2] - 2019-07-01 ### Changed -- Always append a colon after username in basic auth +* Always append a colon after username in basic auth -- Upgrade `rand` dependency version to 0.7 +* Upgrade `rand` dependency version to 0.7 ## [0.2.1] - 2019-06-05 ### Added -- Add license files +* Add license files ## [0.2.0] - 2019-05-12 ### Added -- Allow to send headers in `Camel-Case` form. +* Allow to send headers in `Camel-Case` form. ### Changed -- Upgrade actix-http dependency. +* Upgrade actix-http dependency. ## [0.1.1] - 2019-04-19 ### Added -- Allow to specify server address for http and ws requests. +* Allow to specify server address for http and ws requests. ### Changed -- `ClientRequest::if_true()` and `ClientRequest::if_some()` use instance instead of ref +* `ClientRequest::if_true()` and `ClientRequest::if_some()` use instance instead of ref ## [0.1.0] - 2019-04-16 -- No changes +* No changes ## [0.1.0-alpha.6] - 2019-04-14 ### Changed -- Do not set default headers for websocket request +* Do not set default headers for websocket request ## [0.1.0-alpha.5] - 2019-04-12 ### Changed -- Do not set any default headers +* Do not set any default headers ### Added -- Add Debug impl for BoxedSocket +* Add Debug impl for BoxedSocket ## [0.1.0-alpha.4] - 2019-04-08 ### Changed -- Update actix-http dependency +* Update actix-http dependency ## [0.1.0-alpha.3] - 2019-04-02 ### Added -- Export `MessageBody` type +* Export `MessageBody` type -- `ClientResponse::json()` - Loads and parse `application/json` encoded body +* `ClientResponse::json()` - Loads and parse `application/json` encoded body ### Changed -- `ClientRequest::json()` accepts reference instead of object. +* `ClientRequest::json()` accepts reference instead of object. -- `ClientResponse::body()` does not consume response object. +* `ClientResponse::body()` does not consume response object. -- Renamed `ClientRequest::close_connection()` to `ClientRequest::force_close()` +* Renamed `ClientRequest::close_connection()` to `ClientRequest::force_close()` ## [0.1.0-alpha.2] - 2019-03-29 ### Added -- Per request and session wide request timeout. +* Per request and session wide request timeout. -- Session wide headers. +* Session wide headers. -- Session wide basic and bearer auth. +* Session wide basic and bearer auth. -- Re-export `actix_http::client::Connector`. +* Re-export `actix_http::client::Connector`. ### Changed -- Allow to override request's uri +* Allow to override request's uri -- Export `ws` sub-module with websockets related types +* Export `ws` sub-module with websockets related types ## [0.1.0-alpha.1] - 2019-03-28 -- Initial impl +* Initial impl From de20d21703759d61af0836011210f6d7ffcf10c7 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 22 Dec 2021 08:21:30 +0000 Subject: [PATCH 62/87] use dash hyphenation in markdown --- .github/ISSUE_TEMPLATE/bug_report.md | 4 +- CHANGES.md | 552 +++++++++++++-------------- CODE_OF_CONDUCT.md | 20 +- MIGRATION.md | 156 ++++---- README.md | 60 +-- actix-files/CHANGES.md | 102 ++--- actix-http-test/CHANGES.md | 76 ++-- actix-http/CHANGES.md | 544 +++++++++++++------------- actix-http/README.md | 4 +- actix-multipart/CHANGES.md | 74 ++-- actix-router/CHANGES.md | 102 ++--- actix-test/CHANGES.md | 22 +- actix-web-actors/CHANGES.md | 60 +-- actix-web-codegen/CHANGES.md | 52 +-- awc/CHANGES.md | 170 ++++----- 15 files changed, 999 insertions(+), 999 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2df863ae8..fa06a137a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -33,5 +33,5 @@ Please search on the [Actix Web issue tracker](https://github.com/actix/actix-we ## Your Environment -* Rust Version (I.e, output of `rustc -V`): -* Actix Web Version: +- Rust Version (I.e, output of `rustc -V`): +- Actix Web Version: diff --git a/CHANGES.md b/CHANGES.md index 77ab2e218..8e030819f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,22 +5,22 @@ ## 4.0.0-beta.15 - 2021-12-17 ### Added -* Method on `Responder` trait (`customize`) for customizing responders and `CustomizeResponder` struct. [#2510] -* Implement `Debug` for `DefaultHeaders`. [#2510] +- Method on `Responder` trait (`customize`) for customizing responders and `CustomizeResponder` struct. [#2510] +- Implement `Debug` for `DefaultHeaders`. [#2510] ### Changed -* Align `DefaultHeader` method terminology, deprecating previous methods. [#2510] -* Response service types in `ErrorHandlers` middleware now use `ServiceResponse>` to allow changing the body type. [#2515] -* Both variants in `ErrorHandlerResponse` now use `ServiceResponse>`. [#2515] -* Rename `test::{default_service => simple_service}`. Old name is deprecated. [#2518] -* Rename `test::{read_response_json => call_and_read_body_json}`. Old name is deprecated. [#2518] -* Rename `test::{read_response => call_and_read_body}`. Old name is deprecated. [#2518] -* Relax body type and error bounds on test utilities. [#2518] +- Align `DefaultHeader` method terminology, deprecating previous methods. [#2510] +- Response service types in `ErrorHandlers` middleware now use `ServiceResponse>` to allow changing the body type. [#2515] +- Both variants in `ErrorHandlerResponse` now use `ServiceResponse>`. [#2515] +- Rename `test::{default_service => simple_service}`. Old name is deprecated. [#2518] +- Rename `test::{read_response_json => call_and_read_body_json}`. Old name is deprecated. [#2518] +- Rename `test::{read_response => call_and_read_body}`. Old name is deprecated. [#2518] +- Relax body type and error bounds on test utilities. [#2518] ### Removed -* Top-level `EitherExtractError` export. [#2510] -* Conversion implementations for `either` crate. [#2516] -* `test::load_stream` and `test::load_body`; replace usage with `body::to_bytes`. [#2518] +- Top-level `EitherExtractError` export. [#2510] +- Conversion implementations for `either` crate. [#2516] +- `test::load_stream` and `test::load_body`; replace usage with `body::to_bytes`. [#2518] [#2510]: https://github.com/actix/actix-web/pull/2510 [#2515]: https://github.com/actix/actix-web/pull/2515 @@ -30,31 +30,31 @@ ## 4.0.0-beta.14 - 2021-12-11 ### Added -* Methods on `AcceptLanguage`: `ranked` and `preference`. [#2480] -* `AcceptEncoding` typed header. [#2482] -* `Range` typed header. [#2485] -* `HttpResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] -* `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] -* Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491] -* `HttpRequest::{req_data,req_data_mut}`. [#2487] -* `ServiceResponse::into_parts`. [#2499] +- Methods on `AcceptLanguage`: `ranked` and `preference`. [#2480] +- `AcceptEncoding` typed header. [#2482] +- `Range` typed header. [#2485] +- `HttpResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] +- `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468] +- Connection data set through the `HttpServer::on_connect` callback is now accessible only from the new `HttpRequest::conn_data()` and `ServiceRequest::conn_data()` methods. [#2491] +- `HttpRequest::{req_data,req_data_mut}`. [#2487] +- `ServiceResponse::into_parts`. [#2499] ### Changed -* Rename `Accept::{mime_precedence => ranked}`. [#2480] -* Rename `Accept::{mime_preference => preference}`. [#2480] -* Un-deprecate `App::data_factory`. [#2484] -* `HttpRequest::url_for` no longer constructs URLs with query or fragment components. [#2430] -* Remove `B` (body) type parameter on `App`. [#2493] -* Add `B` (body) type parameter on `Scope`. [#2492] -* Request-local data container is no longer part of a `RequestHead`. Instead it is a distinct part of a `Request`. [#2487] +- Rename `Accept::{mime_precedence => ranked}`. [#2480] +- Rename `Accept::{mime_preference => preference}`. [#2480] +- Un-deprecate `App::data_factory`. [#2484] +- `HttpRequest::url_for` no longer constructs URLs with query or fragment components. [#2430] +- Remove `B` (body) type parameter on `App`. [#2493] +- Add `B` (body) type parameter on `Scope`. [#2492] +- Request-local data container is no longer part of a `RequestHead`. Instead it is a distinct part of a `Request`. [#2487] ### Fixed -* Accept wildcard `*` items in `AcceptLanguage`. [#2480] -* Re-exports `dev::{BodySize, MessageBody, SizedStream}`. They are exposed through the `body` module. [#2468] -* Typed headers containing lists that require one or more items now enforce this minimum. [#2482] +- Accept wildcard `*` items in `AcceptLanguage`. [#2480] +- Re-exports `dev::{BodySize, MessageBody, SizedStream}`. They are exposed through the `body` module. [#2468] +- Typed headers containing lists that require one or more items now enforce this minimum. [#2482] ### Removed -* `ConnectionInfo::get`. [#2487] +- `ConnectionInfo::get`. [#2487] [#2430]: https://github.com/actix/actix-web/pull/2430 [#2468]: https://github.com/actix/actix-web/pull/2468 @@ -71,20 +71,20 @@ ## 4.0.0-beta.13 - 2021-11-30 ### Changed -* Update `actix-tls` to `3.0.0-rc.1`. [#2474] +- Update `actix-tls` to `3.0.0-rc.1`. [#2474] [#2474]: https://github.com/actix/actix-web/pull/2474 ## 4.0.0-beta.12 - 2021-11-22 ### Changed -* Compress middleware's response type is now `AnyBody>`. [#2448] +- Compress middleware's response type is now `AnyBody>`. [#2448] ### Fixed -* Relax `Unpin` bound on `S` (stream) parameter of `HttpResponseBuilder::streaming`. [#2448] +- Relax `Unpin` bound on `S` (stream) parameter of `HttpResponseBuilder::streaming`. [#2448] ### Removed -* `dev::ResponseBody` re-export; is function is replaced by the new `dev::AnyBody` enum. [#2446] +- `dev::ResponseBody` re-export; is function is replaced by the new `dev::AnyBody` enum. [#2446] [#2446]: https://github.com/actix/actix-web/pull/2446 [#2448]: https://github.com/actix/actix-web/pull/2448 @@ -92,11 +92,11 @@ ## 4.0.0-beta.11 - 2021-11-15 ### Added -* Re-export `dev::ServerHandle` from `actix-server`. [#2442] +- Re-export `dev::ServerHandle` from `actix-server`. [#2442] ### Changed -* `ContentType::html` now produces `text/html; charset=utf-8` instead of `text/html`. [#2423] -* Update `actix-server` to `2.0.0-beta.9`. [#2442] +- `ContentType::html` now produces `text/html; charset=utf-8` instead of `text/html`. [#2423] +- Update `actix-server` to `2.0.0-beta.9`. [#2442] [#2423]: https://github.com/actix/actix-web/pull/2423 [#2442]: https://github.com/actix/actix-web/pull/2442 @@ -104,18 +104,18 @@ ## 4.0.0-beta.10 - 2021-10-20 ### Added -* Option to allow `Json` extractor to work without a `Content-Type` header present. [#2362] -* `#[actix_web::test]` macro for setting up tests with a runtime. [#2409] +- Option to allow `Json` extractor to work without a `Content-Type` header present. [#2362] +- `#[actix_web::test]` macro for setting up tests with a runtime. [#2409] ### Changed -* Associated type `FromRequest::Config` was removed. [#2233] -* Inner field made private on `web::Payload`. [#2384] -* `Data::into_inner` and `Data::get_ref` no longer requires `T: Sized`. [#2403] -* Updated rustls to v0.20. [#2414] -* Minimum supported Rust version (MSRV) is now 1.52. +- Associated type `FromRequest::Config` was removed. [#2233] +- Inner field made private on `web::Payload`. [#2384] +- `Data::into_inner` and `Data::get_ref` no longer requires `T: Sized`. [#2403] +- Updated rustls to v0.20. [#2414] +- Minimum supported Rust version (MSRV) is now 1.52. ### Removed -* Useless `ServiceResponse::checked_expr` method. [#2401] +- Useless `ServiceResponse::checked_expr` method. [#2401] [#2233]: https://github.com/actix/actix-web/pull/2233 [#2362]: https://github.com/actix/actix-web/pull/2362 @@ -128,17 +128,17 @@ ## 4.0.0-beta.9 - 2021-09-09 ### Added -* Re-export actix-service `ServiceFactory` in `dev` module. [#2325] +- Re-export actix-service `ServiceFactory` in `dev` module. [#2325] ### Changed -* Compress middleware will return 406 Not Acceptable when no content encoding is acceptable to the client. [#2344] -* Move `BaseHttpResponse` to `dev::Response`. [#2379] -* Enable `TestRequest::param` to accept more than just static strings. [#2172] -* Minimum supported Rust version (MSRV) is now 1.51. +- Compress middleware will return 406 Not Acceptable when no content encoding is acceptable to the client. [#2344] +- Move `BaseHttpResponse` to `dev::Response`. [#2379] +- Enable `TestRequest::param` to accept more than just static strings. [#2172] +- Minimum supported Rust version (MSRV) is now 1.51. ### Fixed -* Fix quality parse error in Accept-Encoding header. [#2344] -* Re-export correct type at `web::HttpResponse`. [#2379] +- Fix quality parse error in Accept-Encoding header. [#2344] +- Re-export correct type at `web::HttpResponse`. [#2379] [#2172]: https://github.com/actix/actix-web/pull/2172 [#2325]: https://github.com/actix/actix-web/pull/2325 @@ -148,18 +148,18 @@ ## 4.0.0-beta.8 - 2021-06-26 ### Added -* Add `ServiceRequest::parts_mut`. [#2177] -* Add extractors for `Uri` and `Method`. [#2263] -* Add extractors for `ConnectionInfo` and `PeerAddr`. [#2263] -* Add `Route::service` for using hand-written services as handlers. [#2262] +- Add `ServiceRequest::parts_mut`. [#2177] +- Add extractors for `Uri` and `Method`. [#2263] +- Add extractors for `ConnectionInfo` and `PeerAddr`. [#2263] +- Add `Route::service` for using hand-written services as handlers. [#2262] ### Changed -* Change compression algorithm features flags. [#2250] -* Deprecate `App::data` and `App::data_factory`. [#2271] -* Smarter extraction of `ConnectionInfo` parts. [#2282] +- Change compression algorithm features flags. [#2250] +- Deprecate `App::data` and `App::data_factory`. [#2271] +- Smarter extraction of `ConnectionInfo` parts. [#2282] ### Fixed -* Scope and Resource middleware can access data items set on their own layer. [#2288] +- Scope and Resource middleware can access data items set on their own layer. [#2288] [#2177]: https://github.com/actix/actix-web/pull/2177 [#2250]: https://github.com/actix/actix-web/pull/2250 @@ -172,23 +172,23 @@ ## 4.0.0-beta.7 - 2021-06-17 ### Added -* `HttpServer::worker_max_blocking_threads` for setting block thread pool. [#2200] +- `HttpServer::worker_max_blocking_threads` for setting block thread pool. [#2200] ### Changed -* Adjusted default JSON payload limit to 2MB (from 32kb) and included size and limits in the `JsonPayloadError::Overflow` error variant. [#2162] +- Adjusted default JSON payload limit to 2MB (from 32kb) and included size and limits in the `JsonPayloadError::Overflow` error variant. [#2162] [#2162]: (https://github.com/actix/actix-web/pull/2162) -* `ServiceResponse::error_response` now uses body type of `Body`. [#2201] -* `ServiceResponse::checked_expr` now returns a `Result`. [#2201] -* Update `language-tags` to `0.3`. -* `ServiceResponse::take_body`. [#2201] -* `ServiceResponse::map_body` closure receives and returns `B` instead of `ResponseBody` types. [#2201] -* All error trait bounds in server service builders have changed from `Into` to `Into>`. [#2253] -* All error trait bounds in message body and stream impls changed from `Into` to `Into>`. [#2253] -* `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226] -* `middleware::normalize` now will not try to normalize URIs with no valid path [#2246] +- `ServiceResponse::error_response` now uses body type of `Body`. [#2201] +- `ServiceResponse::checked_expr` now returns a `Result`. [#2201] +- Update `language-tags` to `0.3`. +- `ServiceResponse::take_body`. [#2201] +- `ServiceResponse::map_body` closure receives and returns `B` instead of `ResponseBody` types. [#2201] +- All error trait bounds in server service builders have changed from `Into` to `Into>`. [#2253] +- All error trait bounds in message body and stream impls changed from `Into` to `Into>`. [#2253] +- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226] +- `middleware::normalize` now will not try to normalize URIs with no valid path [#2246] ### Removed -* `HttpResponse::take_body` and old `HttpResponse::into_body` method that casted body type. [#2201] +- `HttpResponse::take_body` and old `HttpResponse::into_body` method that casted body type. [#2201] [#2200]: https://github.com/actix/actix-web/pull/2200 [#2201]: https://github.com/actix/actix-web/pull/2201 @@ -198,11 +198,11 @@ ## 4.0.0-beta.6 - 2021-04-17 ### Added -* `HttpResponse` and `HttpResponseBuilder` structs. [#2065] +- `HttpResponse` and `HttpResponseBuilder` structs. [#2065] ### Changed -* Most error types are now marked `#[non_exhaustive]`. [#2148] -* Methods on `ContentDisposition` that took `T: AsRef` now take `impl AsRef`. +- Most error types are now marked `#[non_exhaustive]`. [#2148] +- Methods on `ContentDisposition` that took `T: AsRef` now take `impl AsRef`. [#2065]: https://github.com/actix/actix-web/pull/2065 [#2148]: https://github.com/actix/actix-web/pull/2148 @@ -210,20 +210,20 @@ ## 4.0.0-beta.5 - 2021-04-02 ### Added -* `Header` extractor for extracting common HTTP headers in handlers. [#2094] -* Added `TestServer::client_headers` method. [#2097] +- `Header` extractor for extracting common HTTP headers in handlers. [#2094] +- Added `TestServer::client_headers` method. [#2097] ### Fixed -* Double ampersand in Logger format is escaped correctly. [#2067] +- Double ampersand in Logger format is escaped correctly. [#2067] ### Changed -* `CustomResponder` would return error as `HttpResponse` when `CustomResponder::with_header` failed +- `CustomResponder` would return error as `HttpResponse` when `CustomResponder::with_header` failed instead of skipping. (Only the first error is kept when multiple error occur) [#2093] ### Removed -* The `client` mod was removed. Clients should now use `awc` directly. +- The `client` mod was removed. Clients should now use `awc` directly. [871ca5e4](https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7) -* Integration testing was moved to new `actix-test` crate. Namely these items from the `test` +- Integration testing was moved to new `actix-test` crate. Namely these items from the `test` module: `TestServer`, `TestServerConfig`, `start`, `start_with`, and `unused_addr`. [#2112] [#2067]: https://github.com/actix/actix-web/pull/2067 @@ -235,8 +235,8 @@ ## 4.0.0-beta.4 - 2021-03-09 ### Changed -* Feature `cookies` is now optional and enabled by default. [#1981] -* `JsonBody::new` returns a default limit of 32kB to be consistent with `JsonConfig` and the default +- Feature `cookies` is now optional and enabled by default. [#1981] +- `JsonBody::new` returns a default limit of 32kB to be consistent with `JsonConfig` and the default behaviour of the `web::Json` extractor. [#2010] [#1981]: https://github.com/actix/actix-web/pull/1981 @@ -244,36 +244,36 @@ ## 4.0.0-beta.3 - 2021-02-10 -* Update `actix-web-codegen` to `0.5.0-beta.1`. +- Update `actix-web-codegen` to `0.5.0-beta.1`. ## 4.0.0-beta.2 - 2021-02-10 ### Added -* The method `Either, web::Form>::into_inner()` which returns the inner type for +- The method `Either, web::Form>::into_inner()` which returns the inner type for whichever variant was created. Also works for `Either, web::Json>`. [#1894] -* Add `services!` macro for helping register multiple services to `App`. [#1933] -* Enable registering a vec of services of the same type to `App` [#1933] +- Add `services!` macro for helping register multiple services to `App`. [#1933] +- Enable registering a vec of services of the same type to `App` [#1933] ### Changed -* Rework `Responder` trait to be sync and returns `Response`/`HttpResponse` directly. +- Rework `Responder` trait to be sync and returns `Response`/`HttpResponse` directly. Making it simpler and more performant. [#1891] -* `ServiceRequest::into_parts` and `ServiceRequest::from_parts` can no longer fail. [#1893] -* `ServiceRequest::from_request` can no longer fail. [#1893] -* Our `Either` type now uses `Left`/`Right` variants (instead of `A`/`B`) [#1894] -* `test::{call_service, read_response, read_response_json, send_request}` take `&Service` +- `ServiceRequest::into_parts` and `ServiceRequest::from_parts` can no longer fail. [#1893] +- `ServiceRequest::from_request` can no longer fail. [#1893] +- Our `Either` type now uses `Left`/`Right` variants (instead of `A`/`B`) [#1894] +- `test::{call_service, read_response, read_response_json, send_request}` take `&Service` in argument [#1905] -* `App::wrap_fn`, `Resource::wrap_fn` and `Scope::wrap_fn` provide `&Service` in closure +- `App::wrap_fn`, `Resource::wrap_fn` and `Scope::wrap_fn` provide `&Service` in closure argument. [#1905] -* `web::block` no longer requires the output is a Result. [#1957] +- `web::block` no longer requires the output is a Result. [#1957] ### Fixed -* Multiple calls to `App::data` with the same type now keeps the latest call's data. [#1906] +- Multiple calls to `App::data` with the same type now keeps the latest call's data. [#1906] ### Removed -* Public field of `web::Path` has been made private. [#1894] -* Public field of `web::Query` has been made private. [#1894] -* `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869] -* `AppService::set_service_data`; for custom HTTP service factories adding application data, use the +- Public field of `web::Path` has been made private. [#1894] +- Public field of `web::Query` has been made private. [#1894] +- `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869] +- `AppService::set_service_data`; for custom HTTP service factories adding application data, use the layered data model by calling `ServiceRequest::add_data_container` when handling requests instead. [#1906] @@ -289,26 +289,26 @@ ## 4.0.0-beta.1 - 2021-01-07 ### Added -* `Compat` middleware enabling generic response body/error type of middlewares like `Logger` and +- `Compat` middleware enabling generic response body/error type of middlewares like `Logger` and `Compress` to be used in `middleware::Condition` and `Resource`, `Scope` services. [#1865] ### Changed -* Update `actix-*` dependencies to tokio `1.0` based versions. [#1813] -* Bumped `rand` to `0.8`. -* Update `rust-tls` to `0.19`. [#1813] -* Rename `Handler` to `HandlerService` and rename `Factory` to `Handler`. [#1852] -* The default `TrailingSlash` is now `Trim`, in line with existing documentation. See migration +- Update `actix-*` dependencies to tokio `1.0` based versions. [#1813] +- Bumped `rand` to `0.8`. +- Update `rust-tls` to `0.19`. [#1813] +- Rename `Handler` to `HandlerService` and rename `Factory` to `Handler`. [#1852] +- The default `TrailingSlash` is now `Trim`, in line with existing documentation. See migration guide for implications. [#1875] -* Rename `DefaultHeaders::{content_type => add_content_type}`. [#1875] -* MSRV is now 1.46.0. +- Rename `DefaultHeaders::{content_type => add_content_type}`. [#1875] +- MSRV is now 1.46.0. ### Fixed -* Added the underlying parse error to `test::read_body_json`'s panic message. [#1812] +- Added the underlying parse error to `test::read_body_json`'s panic message. [#1812] ### Removed -* Public modules `middleware::{normalize, err_handlers}`. All necessary middleware structs are now +- Public modules `middleware::{normalize, err_handlers}`. All necessary middleware structs are now exposed directly by the `middleware` module. -* Remove `actix-threadpool` as dependency. `actix_threadpool::BlockingError` error type can be imported +- Remove `actix-threadpool` as dependency. `actix_threadpool::BlockingError` error type can be imported from `actix_web::error` module. [#1878] [#1812]: https://github.com/actix/actix-web/pull/1812 @@ -321,16 +321,16 @@ ## 3.3.3 - 2021-12-18 ### Changed -* Soft-deprecate `NormalizePath::default()`, noting upcoming behavior change in v4. [#2529] +- Soft-deprecate `NormalizePath::default()`, noting upcoming behavior change in v4. [#2529] [#2529]: https://github.com/actix/actix-web/pull/2529 ## 3.3.2 - 2020-12-01 ### Fixed -* Removed an occasional `unwrap` on `None` panic in `NormalizePathNormalization`. [#1762] -* Fix `match_pattern()` returning `None` for scope with empty path resource. [#1798] -* Increase minimum `socket2` version. [#1803] +- Removed an occasional `unwrap` on `None` panic in `NormalizePathNormalization`. [#1762] +- Fix `match_pattern()` returning `None` for scope with empty path resource. [#1798] +- Increase minimum `socket2` version. [#1803] [#1762]: https://github.com/actix/actix-web/pull/1762 [#1798]: https://github.com/actix/actix-web/pull/1798 @@ -338,15 +338,15 @@ ## 3.3.1 - 2020-11-29 -* Ensure `actix-http` dependency uses same `serde_urlencoded`. +- Ensure `actix-http` dependency uses same `serde_urlencoded`. ## 3.3.0 - 2020-11-25 ### Added -* Add `Either` extractor helper. [#1788] +- Add `Either` extractor helper. [#1788] ### Changed -* Upgrade `serde_urlencoded` to `0.7`. [#1773] +- Upgrade `serde_urlencoded` to `0.7`. [#1773] [#1773]: https://github.com/actix/actix-web/pull/1773 [#1788]: https://github.com/actix/actix-web/pull/1788 @@ -354,17 +354,17 @@ ## 3.2.0 - 2020-10-30 ### Added -* Implement `exclude_regex` for Logger middleware. [#1723] -* Add request-local data extractor `web::ReqData`. [#1748] -* Add ability to register closure for request middleware logging. [#1749] -* Add `app_data` to `ServiceConfig`. [#1757] -* Expose `on_connect` for access to the connection stream before request is handled. [#1754] +- Implement `exclude_regex` for Logger middleware. [#1723] +- Add request-local data extractor `web::ReqData`. [#1748] +- Add ability to register closure for request middleware logging. [#1749] +- Add `app_data` to `ServiceConfig`. [#1757] +- Expose `on_connect` for access to the connection stream before request is handled. [#1754] ### Changed -* Updated actix-web-codegen dependency for access to new `#[route(...)]` multi-method macro. -* Print non-configured `Data` type when attempting extraction. [#1743] -* Re-export bytes::Buf{Mut} in web module. [#1750] -* Upgrade `pin-project` to `1.0`. +- Updated actix-web-codegen dependency for access to new `#[route(...)]` multi-method macro. +- Print non-configured `Data` type when attempting extraction. [#1743] +- Re-export bytes::Buf{Mut} in web module. [#1750] +- Upgrade `pin-project` to `1.0`. [#1723]: https://github.com/actix/actix-web/pull/1723 [#1743]: https://github.com/actix/actix-web/pull/1743 @@ -376,13 +376,13 @@ ## 3.1.0 - 2020-09-29 ### Changed -* Add `TrailingSlash::MergeOnly` behaviour to `NormalizePath`, which allows `NormalizePath` +- Add `TrailingSlash::MergeOnly` behaviour to `NormalizePath`, which allows `NormalizePath` to retain any trailing slashes. [#1695] -* Remove bound `std::marker::Sized` from `web::Data` to support storing `Arc` +- Remove bound `std::marker::Sized` from `web::Data` to support storing `Arc` via `web::Data::from` [#1710] ### Fixed -* `ResourceMap` debug printing is no longer infinitely recursive. [#1708] +- `ResourceMap` debug printing is no longer infinitely recursive. [#1708] [#1695]: https://github.com/actix/actix-web/pull/1695 [#1708]: https://github.com/actix/actix-web/pull/1708 @@ -391,33 +391,33 @@ ## 3.0.2 - 2020-09-15 ### Fixed -* `NormalizePath` when used with `TrailingSlash::Trim` no longer trims the root path "/". [#1678] +- `NormalizePath` when used with `TrailingSlash::Trim` no longer trims the root path "/". [#1678] [#1678]: https://github.com/actix/actix-web/pull/1678 ## 3.0.1 - 2020-09-13 ### Changed -* `middleware::normalize::TrailingSlash` enum is now accessible. [#1673] +- `middleware::normalize::TrailingSlash` enum is now accessible. [#1673] [#1673]: https://github.com/actix/actix-web/pull/1673 ## 3.0.0 - 2020-09-11 -* No significant changes from `3.0.0-beta.4`. +- No significant changes from `3.0.0-beta.4`. ## 3.0.0-beta.4 - 2020-09-09 ### Added -* `middleware::NormalizePath` now has configurable behavior for either always having a trailing +- `middleware::NormalizePath` now has configurable behavior for either always having a trailing slash, or as the new addition, always trimming trailing slashes. [#1639] ### Changed -* Update actix-codec and actix-utils dependencies. [#1634] -* `FormConfig` and `JsonConfig` configurations are now also considered when set +- Update actix-codec and actix-utils dependencies. [#1634] +- `FormConfig` and `JsonConfig` configurations are now also considered when set using `App::data`. [#1641] -* `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`. [#1655] -* `HttpServer::maxconnrate` is renamed to the more expressive +- `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`. [#1655] +- `HttpServer::maxconnrate` is renamed to the more expressive `HttpServer::max_connection_rate`. [#1655] [#1639]: https://github.com/actix/actix-web/pull/1639 @@ -427,22 +427,22 @@ ## 3.0.0-beta.3 - 2020-08-17 ### Changed -* Update `rustls` to 0.18 +- Update `rustls` to 0.18 ## 3.0.0-beta.2 - 2020-08-17 ### Changed -* `PayloadConfig` is now also considered in `Bytes` and `String` extractors when set +- `PayloadConfig` is now also considered in `Bytes` and `String` extractors when set using `App::data`. [#1610] -* `web::Path` now has a public representation: `web::Path(pub T)` that enables +- `web::Path` now has a public representation: `web::Path(pub T)` that enables destructuring. [#1594] -* `ServiceRequest::app_data` allows retrieval of non-Data data without splitting into parts to +- `ServiceRequest::app_data` allows retrieval of non-Data data without splitting into parts to access `HttpRequest` which already allows this. [#1618] -* Re-export all error types from `awc`. [#1621] -* MSRV is now 1.42.0. +- Re-export all error types from `awc`. [#1621] +- MSRV is now 1.42.0. ### Fixed -* Memory leak of app data in pooled requests. [#1609] +- Memory leak of app data in pooled requests. [#1609] [#1594]: https://github.com/actix/actix-web/pull/1594 [#1609]: https://github.com/actix/actix-web/pull/1609 @@ -453,29 +453,29 @@ ## 3.0.0-beta.1 - 2020-07-13 ### Added -* Re-export `actix_rt::main` as `actix_web::main`. -* `HttpRequest::match_pattern` and `ServiceRequest::match_pattern` for extracting the matched +- Re-export `actix_rt::main` as `actix_web::main`. +- `HttpRequest::match_pattern` and `ServiceRequest::match_pattern` for extracting the matched resource pattern. -* `HttpRequest::match_name` and `ServiceRequest::match_name` for extracting matched resource name. +- `HttpRequest::match_name` and `ServiceRequest::match_name` for extracting matched resource name. ### Changed -* Fix actix_http::h1::dispatcher so it returns when HW_BUFFER_SIZE is reached. Should reduce peak memory consumption during large uploads. [#1550] -* Migrate cookie handling to `cookie` crate. Actix-web no longer requires `ring` dependency. -* MSRV is now 1.41.1 +- Fix actix_http::h1::dispatcher so it returns when HW_BUFFER_SIZE is reached. Should reduce peak memory consumption during large uploads. [#1550] +- Migrate cookie handling to `cookie` crate. Actix-web no longer requires `ring` dependency. +- MSRV is now 1.41.1 ### Fixed -* `NormalizePath` improved consistency when path needs slashes added _and_ removed. +- `NormalizePath` improved consistency when path needs slashes added _and_ removed. ## 3.0.0-alpha.3 - 2020-05-21 ### Added -* Add option to create `Data` from `Arc` [#1509] +- Add option to create `Data` from `Arc` [#1509] ### Changed -* Resources and Scopes can now access non-overridden data types set on App (or containing scopes) when setting their own data. [#1486] -* Fix audit issue logging by default peer address [#1485] -* Bump minimum supported Rust version to 1.40 -* Replace deprecated `net2` crate with `socket2` +- Resources and Scopes can now access non-overridden data types set on App (or containing scopes) when setting their own data. [#1486] +- Fix audit issue logging by default peer address [#1485] +- Bump minimum supported Rust version to 1.40 +- Replace deprecated `net2` crate with `socket2` [#1485]: https://github.com/actix/actix-web/pull/1485 [#1509]: https://github.com/actix/actix-web/pull/1509 @@ -484,10 +484,10 @@ ### Changed -* `{Resource,Scope}::default_service(f)` handlers now support app data extraction. [#1452] -* Implement `std::error::Error` for our custom errors [#1422] -* NormalizePath middleware now appends trailing / so that routes of form /example/ respond to /example requests. [#1433] -* Remove the `failure` feature and support. +- `{Resource,Scope}::default_service(f)` handlers now support app data extraction. [#1452] +- Implement `std::error::Error` for our custom errors [#1422] +- NormalizePath middleware now appends trailing / so that routes of form /example/ respond to /example requests. [#1433] +- Remove the `failure` feature and support. [#1422]: https://github.com/actix/actix-web/pull/1422 [#1433]: https://github.com/actix/actix-web/pull/1433 @@ -499,16 +499,16 @@ ### Added -* Add helper function for creating routes with `TRACE` method guard `web::trace()` -* Add convenience functions `test::read_body_json()` and `test::TestRequest::send_request()` for testing. +- Add helper function for creating routes with `TRACE` method guard `web::trace()` +- Add convenience functions `test::read_body_json()` and `test::TestRequest::send_request()` for testing. ### Changed -* Use `sha-1` crate instead of unmaintained `sha1` crate -* Skip empty chunks when returning response from a `Stream` [#1308] -* Update the `time` dependency to 0.2.7 -* Update `actix-tls` dependency to 2.0.0-alpha.1 -* Update `rustls` dependency to 0.17 +- Use `sha-1` crate instead of unmaintained `sha1` crate +- Skip empty chunks when returning response from a `Stream` [#1308] +- Update the `time` dependency to 0.2.7 +- Update `actix-tls` dependency to 2.0.0-alpha.1 +- Update `rustls` dependency to 0.17 [#1308]: https://github.com/actix/actix-web/pull/1308 @@ -516,408 +516,408 @@ ### Changed -* Rename `HttpServer::start()` to `HttpServer::run()` +- Rename `HttpServer::start()` to `HttpServer::run()` -* Allow to gracefully stop test server via `TestServer::stop()` +- Allow to gracefully stop test server via `TestServer::stop()` -* Allow to specify multi-patterns for resources +- Allow to specify multi-patterns for resources ## [2.0.0-rc] - 2019-12-20 ### Changed -* Move `BodyEncoding` to `dev` module #1220 +- Move `BodyEncoding` to `dev` module #1220 -* Allow to set `peer_addr` for TestRequest #1074 +- Allow to set `peer_addr` for TestRequest #1074 -* Make web::Data deref to Arc #1214 +- Make web::Data deref to Arc #1214 -* Rename `App::register_data()` to `App::app_data()` +- Rename `App::register_data()` to `App::app_data()` -* `HttpRequest::app_data()` returns `Option<&T>` instead of `Option<&Data>` +- `HttpRequest::app_data()` returns `Option<&T>` instead of `Option<&Data>` ### Fixed -* Fix `AppConfig::secure()` is always false. #1202 +- Fix `AppConfig::secure()` is always false. #1202 ## [2.0.0-alpha.6] - 2019-12-15 ### Fixed -* Fixed compilation with default features off +- Fixed compilation with default features off ## [2.0.0-alpha.5] - 2019-12-13 ### Added -* Add test server, `test::start()` and `test::start_with()` +- Add test server, `test::start()` and `test::start_with()` ## [2.0.0-alpha.4] - 2019-12-08 ### Deleted -* Delete HttpServer::run(), it is not useful with async/await +- Delete HttpServer::run(), it is not useful with async/await ## [2.0.0-alpha.3] - 2019-12-07 ### Changed -* Migrate to tokio 0.2 +- Migrate to tokio 0.2 ## [2.0.0-alpha.1] - 2019-11-22 ### Changed -* Migrated to `std::future` +- Migrated to `std::future` -* Remove implementation of `Responder` for `()`. (#1167) +- Remove implementation of `Responder` for `()`. (#1167) ## [1.0.9] - 2019-11-14 ### Added -* Add `Payload::into_inner` method and make stored `def::Payload` public. (#1110) +- Add `Payload::into_inner` method and make stored `def::Payload` public. (#1110) ### Changed -* Support `Host` guards when the `Host` header is unset (e.g. HTTP/2 requests) (#1129) +- Support `Host` guards when the `Host` header is unset (e.g. HTTP/2 requests) (#1129) ## [1.0.8] - 2019-09-25 ### Added -* Add `Scope::register_data` and `Resource::register_data` methods, parallel to +- Add `Scope::register_data` and `Resource::register_data` methods, parallel to `App::register_data`. -* Add `middleware::Condition` that conditionally enables another middleware +- Add `middleware::Condition` that conditionally enables another middleware -* Allow to re-construct `ServiceRequest` from `HttpRequest` and `Payload` +- Allow to re-construct `ServiceRequest` from `HttpRequest` and `Payload` -* Add `HttpServer::listen_uds` for ability to listen on UDS FD rather than path, +- Add `HttpServer::listen_uds` for ability to listen on UDS FD rather than path, which is useful for example with systemd. ### Changed -* Make UrlEncodedError::Overflow more informative +- Make UrlEncodedError::Overflow more informative -* Use actix-testing for testing utils +- Use actix-testing for testing utils ## [1.0.7] - 2019-08-29 ### Fixed -* Request Extensions leak #1062 +- Request Extensions leak #1062 ## [1.0.6] - 2019-08-28 ### Added -* Re-implement Host predicate (#989) +- Re-implement Host predicate (#989) -* Form implements Responder, returning a `application/x-www-form-urlencoded` response +- Form implements Responder, returning a `application/x-www-form-urlencoded` response -* Add `into_inner` to `Data` +- Add `into_inner` to `Data` -* Add `test::TestRequest::set_form()` convenience method to automatically serialize data and set +- Add `test::TestRequest::set_form()` convenience method to automatically serialize data and set the header in test requests. ### Changed -* `Query` payload made `pub`. Allows user to pattern-match the payload. +- `Query` payload made `pub`. Allows user to pattern-match the payload. -* Enable `rust-tls` feature for client #1045 +- Enable `rust-tls` feature for client #1045 -* Update serde_urlencoded to 0.6.1 +- Update serde_urlencoded to 0.6.1 -* Update url to 2.1 +- Update url to 2.1 ## [1.0.5] - 2019-07-18 ### Added -* Unix domain sockets (HttpServer::bind_uds) #92 +- Unix domain sockets (HttpServer::bind_uds) #92 -* Actix now logs errors resulting in "internal server error" responses always, with the `error` +- Actix now logs errors resulting in "internal server error" responses always, with the `error` logging level ### Fixed -* Restored logging of errors through the `Logger` middleware +- Restored logging of errors through the `Logger` middleware ## [1.0.4] - 2019-07-17 ### Added -* Add `Responder` impl for `(T, StatusCode) where T: Responder` +- Add `Responder` impl for `(T, StatusCode) where T: Responder` -* Allow to access app's resource map via +- Allow to access app's resource map via `ServiceRequest::resource_map()` and `HttpRequest::resource_map()` methods. ### Changed -* Upgrade `rand` dependency version to 0.7 +- Upgrade `rand` dependency version to 0.7 ## [1.0.3] - 2019-06-28 ### Added -* Support asynchronous data factories #850 +- Support asynchronous data factories #850 ### Changed -* Use `encoding_rs` crate instead of unmaintained `encoding` crate +- Use `encoding_rs` crate instead of unmaintained `encoding` crate ## [1.0.2] - 2019-06-17 ### Changed -* Move cors middleware to `actix-cors` crate. +- Move cors middleware to `actix-cors` crate. -* Move identity middleware to `actix-identity` crate. +- Move identity middleware to `actix-identity` crate. ## [1.0.1] - 2019-06-17 ### Added -* Add support for PathConfig #903 +- Add support for PathConfig #903 -* Add `middleware::identity::RequestIdentity` trait to `get_identity` from `HttpMessage`. +- Add `middleware::identity::RequestIdentity` trait to `get_identity` from `HttpMessage`. ### Changed -* Move cors middleware to `actix-cors` crate. +- Move cors middleware to `actix-cors` crate. -* Move identity middleware to `actix-identity` crate. +- Move identity middleware to `actix-identity` crate. -* Disable default feature `secure-cookies`. +- Disable default feature `secure-cookies`. -* Allow to test an app that uses async actors #897 +- Allow to test an app that uses async actors #897 -* Re-apply patch from #637 #894 +- Re-apply patch from #637 #894 ### Fixed -* HttpRequest::url_for is broken with nested scopes #915 +- HttpRequest::url_for is broken with nested scopes #915 ## [1.0.0] - 2019-06-05 ### Added -* Add `Scope::configure()` method. +- Add `Scope::configure()` method. -* Add `ServiceRequest::set_payload()` method. +- Add `ServiceRequest::set_payload()` method. -* Add `test::TestRequest::set_json()` convenience method to automatically +- Add `test::TestRequest::set_json()` convenience method to automatically serialize data and set header in test requests. -* Add macros for head, options, trace, connect and patch http methods +- Add macros for head, options, trace, connect and patch http methods ### Changed -* Drop an unnecessary `Option<_>` indirection around `ServerBuilder` from `HttpServer`. #863 +- Drop an unnecessary `Option<_>` indirection around `ServerBuilder` from `HttpServer`. #863 ### Fixed -* Fix Logger request time format, and use rfc3339. #867 +- Fix Logger request time format, and use rfc3339. #867 -* Clear http requests pool on app service drop #860 +- Clear http requests pool on app service drop #860 ## [1.0.0-rc] - 2019-05-18 ### Added -* Add `Query::from_query()` to extract parameters from a query string. #846 -* `QueryConfig`, similar to `JsonConfig` for customizing error handling of query extractors. +- Add `Query::from_query()` to extract parameters from a query string. #846 +- `QueryConfig`, similar to `JsonConfig` for customizing error handling of query extractors. ### Changed -* `JsonConfig` is now `Send + Sync`, this implies that `error_handler` must be `Send + Sync` too. +- `JsonConfig` is now `Send + Sync`, this implies that `error_handler` must be `Send + Sync` too. ### Fixed -* Codegen with parameters in the path only resolves the first registered endpoint #841 +- Codegen with parameters in the path only resolves the first registered endpoint #841 ## [1.0.0-beta.4] - 2019-05-12 ### Added -* Allow to set/override app data on scope level +- Allow to set/override app data on scope level ### Changed -* `App::configure` take an `FnOnce` instead of `Fn` -* Upgrade actix-net crates +- `App::configure` take an `FnOnce` instead of `Fn` +- Upgrade actix-net crates ## [1.0.0-beta.3] - 2019-05-04 ### Added -* Add helper function for executing futures `test::block_fn()` +- Add helper function for executing futures `test::block_fn()` ### Changed -* Extractor configuration could be registered with `App::data()` +- Extractor configuration could be registered with `App::data()` or with `Resource::data()` #775 -* Route data is unified with app data, `Route::data()` moved to resource +- Route data is unified with app data, `Route::data()` moved to resource level to `Resource::data()` -* CORS handling without headers #702 +- CORS handling without headers #702 -* Allow constructing `Data` instances to avoid double `Arc` for `Send + Sync` types. +- Allow constructing `Data` instances to avoid double `Arc` for `Send + Sync` types. ### Fixed -* Fix `NormalizePath` middleware impl #806 +- Fix `NormalizePath` middleware impl #806 ### Deleted -* `App::data_factory()` is deleted. +- `App::data_factory()` is deleted. ## [1.0.0-beta.2] - 2019-04-24 ### Added -* Add raw services support via `web::service()` +- Add raw services support via `web::service()` -* Add helper functions for reading response body `test::read_body()` +- Add helper functions for reading response body `test::read_body()` -* Add support for `remainder match` (i.e "/path/{tail}*") +- Add support for `remainder match` (i.e "/path/{tail}*") -* Extend `Responder` trait, allow to override status code and headers. +- Extend `Responder` trait, allow to override status code and headers. -* Store visit and login timestamp in the identity cookie #502 +- Store visit and login timestamp in the identity cookie #502 ### Changed -* `.to_async()` handler can return `Responder` type #792 +- `.to_async()` handler can return `Responder` type #792 ### Fixed -* Fix async web::Data factory handling +- Fix async web::Data factory handling ## [1.0.0-beta.1] - 2019-04-20 ### Added -* Add helper functions for reading test response body, +- Add helper functions for reading test response body, `test::read_response()` and test::read_response_json()` -* Add `.peer_addr()` #744 +- Add `.peer_addr()` #744 -* Add `NormalizePath` middleware +- Add `NormalizePath` middleware ### Changed -* Rename `RouterConfig` to `ServiceConfig` +- Rename `RouterConfig` to `ServiceConfig` -* Rename `test::call_success` to `test::call_service` +- Rename `test::call_success` to `test::call_service` -* Removed `ServiceRequest::from_parts()` as it is unsafe to create from parts. +- Removed `ServiceRequest::from_parts()` as it is unsafe to create from parts. -* `CookieIdentityPolicy::max_age()` accepts value in seconds +- `CookieIdentityPolicy::max_age()` accepts value in seconds ### Fixed -* Fixed `TestRequest::app_data()` +- Fixed `TestRequest::app_data()` ## [1.0.0-alpha.6] - 2019-04-14 ### Changed -* Allow using any service as default service. +- Allow using any service as default service. -* Remove generic type for request payload, always use default. +- Remove generic type for request payload, always use default. -* Removed `Decompress` middleware. Bytes, String, Json, Form extractors +- Removed `Decompress` middleware. Bytes, String, Json, Form extractors automatically decompress payload. -* Make extractor config type explicit. Add `FromRequest::Config` associated type. +- Make extractor config type explicit. Add `FromRequest::Config` associated type. ## [1.0.0-alpha.5] - 2019-04-12 ### Added -* Added async io `TestBuffer` for testing. +- Added async io `TestBuffer` for testing. ### Deleted -* Removed native-tls support +- Removed native-tls support ## [1.0.0-alpha.4] - 2019-04-08 ### Added -* `App::configure()` allow to offload app configuration to different methods +- `App::configure()` allow to offload app configuration to different methods -* Added `URLPath` option for logger +- Added `URLPath` option for logger -* Added `ServiceRequest::app_data()`, returns `Data` +- Added `ServiceRequest::app_data()`, returns `Data` -* Added `ServiceFromRequest::app_data()`, returns `Data` +- Added `ServiceFromRequest::app_data()`, returns `Data` ### Changed -* `FromRequest` trait refactoring +- `FromRequest` trait refactoring -* Move multipart support to actix-multipart crate +- Move multipart support to actix-multipart crate ### Fixed -* Fix body propagation in Response::from_error. #760 +- Fix body propagation in Response::from_error. #760 ## [1.0.0-alpha.3] - 2019-04-02 ### Changed -* Renamed `TestRequest::to_service()` to `TestRequest::to_srv_request()` +- Renamed `TestRequest::to_service()` to `TestRequest::to_srv_request()` -* Renamed `TestRequest::to_response()` to `TestRequest::to_srv_response()` +- Renamed `TestRequest::to_response()` to `TestRequest::to_srv_response()` -* Removed `Deref` impls +- Removed `Deref` impls ### Removed -* Removed unused `actix_web::web::md()` +- Removed unused `actix_web::web::md()` ## [1.0.0-alpha.2] - 2019-03-29 ### Added -* Rustls support +- Rustls support ### Changed -* Use forked cookie +- Use forked cookie -* Multipart::Field renamed to MultipartField +- Multipart::Field renamed to MultipartField ## [1.0.0-alpha.1] - 2019-03-28 ### Changed -* Complete architecture re-design. +- Complete architecture re-design. -* Return 405 response if no matching route found within resource #538 +- Return 405 response if no matching route found within resource #538 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index ae97b3240..dbd092095 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities diff --git a/MIGRATION.md b/MIGRATION.md index d53bd7bf8..338a04389 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,6 +1,6 @@ ## Unreleased -* The default `NormalizePath` behavior now strips trailing slashes by default. This was +- The default `NormalizePath` behavior now strips trailing slashes by default. This was previously documented to be the case in v3 but the behavior now matches. The effect is that routes defined with trailing slashes will become inaccessible when using `NormalizePath::default()`. As such, calling `NormalizePath::default()` will log a warning. @@ -11,9 +11,9 @@ Alternatively, explicitly require trailing slashes: `NormalizePath::new(TrailingSlash::Always)`. -* The `type Config` of `FromRequest` was removed. +- The `type Config` of `FromRequest` was removed. -* Feature flag `compress` has been split into its supported algorithm (brotli, gzip, zstd). +- Feature flag `compress` has been split into its supported algorithm (brotli, gzip, zstd). By default all compression algorithms are enabled. To select algorithm you want to include with `middleware::Compress` use following flags: - `compress-brotli` @@ -28,30 +28,30 @@ ## 3.0.0 -* The return type for `ServiceRequest::app_data::()` was changed from returning a `Data` to +- The return type for `ServiceRequest::app_data::()` was changed from returning a `Data` to simply a `T`. To access a `Data` use `ServiceRequest::app_data::>()`. -* Cookie handling has been offloaded to the `cookie` crate: +- Cookie handling has been offloaded to the `cookie` crate: * `USERINFO_ENCODE_SET` is no longer exposed. Percent-encoding is still supported; check docs. * Some types now require lifetime parameters. -* The time crate was updated to `v0.2`, a major breaking change to the time crate, which affects +- The time crate was updated to `v0.2`, a major breaking change to the time crate, which affects any `actix-web` method previously expecting a time v0.1 input. -* Setting a cookie's SameSite property, explicitly, to `SameSite::None` will now +- Setting a cookie's SameSite property, explicitly, to `SameSite::None` will now result in `SameSite=None` being sent with the response Set-Cookie header. To create a cookie without a SameSite attribute, remove any calls setting same_site. -* actix-http support for Actors messages was moved to actix-http crate and is enabled +- actix-http support for Actors messages was moved to actix-http crate and is enabled with feature `actors` -* content_length function is removed from actix-http. +- content_length function is removed from actix-http. You can set Content-Length by normally setting the response body or calling no_chunking function. -* `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a +- `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a `u64` instead of a `usize`. -* Code that was using `path.` to access a `web::Path<(A, B, C)>`s elements now needs to use +- Code that was using `path.` to access a `web::Path<(A, B, C)>`s elements now needs to use destructuring or `.into_inner()`. For example: ```rust @@ -71,35 +71,35 @@ } ``` -* `middleware::NormalizePath` can now also be configured to trim trailing slashes instead of always keeping one. +- `middleware::NormalizePath` can now also be configured to trim trailing slashes instead of always keeping one. It will need `middleware::normalize::TrailingSlash` when being constructed with `NormalizePath::new(...)`, or for an easier migration you can replace `wrap(middleware::NormalizePath)` with `wrap(middleware::NormalizePath::new(TrailingSlash::MergeOnly))`. -* `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`. +- `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`. -* `HttpServer::maxconnrate` is renamed to the more expressive `HttpServer::max_connection_rate`. +- `HttpServer::maxconnrate` is renamed to the more expressive `HttpServer::max_connection_rate`. ## 2.0.0 -* `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to +- `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to `.await` on `run` method result, in that case it awaits server exit. -* `App::register_data()` renamed to `App::app_data()` and accepts any type `T: 'static`. +- `App::register_data()` renamed to `App::app_data()` and accepts any type `T: 'static`. Stored data is available via `HttpRequest::app_data()` method at runtime. -* Extractor configuration must be registered with `App::app_data()` instead of `App::data()` +- Extractor configuration must be registered with `App::app_data()` instead of `App::data()` -* Sync handlers has been removed. `.to_async()` method has been renamed to `.to()` +- Sync handlers has been removed. `.to_async()` method has been renamed to `.to()` replace `fn` with `async fn` to convert sync handler to async -* `actix_http_test::TestServer` moved to `actix_web::test` module. To start +- `actix_http_test::TestServer` moved to `actix_web::test` module. To start test server use `test::start()` or `test_start_with_config()` methods -* `ResponseError` trait has been reafctored. `ResponseError::error_response()` renders +- `ResponseError` trait has been reafctored. `ResponseError::error_response()` renders http response. -* Feature `rust-tls` renamed to `rustls` +- Feature `rust-tls` renamed to `rustls` instead of @@ -113,7 +113,7 @@ actix-web = { version = "2.0.0", features = ["rustls"] } ``` -* Feature `ssl` renamed to `openssl` +- Feature `ssl` renamed to `openssl` instead of @@ -126,11 +126,11 @@ ```rust actix-web = { version = "2.0.0", features = ["openssl"] } ``` -* `Cors` builder now requires that you call `.finish()` to construct the middleware +- `Cors` builder now requires that you call `.finish()` to construct the middleware ## 1.0.1 -* Cors middleware has been moved to `actix-cors` crate +- Cors middleware has been moved to `actix-cors` crate instead of @@ -144,7 +144,7 @@ use actix_cors::Cors; ``` -* Identity middleware has been moved to `actix-identity` crate +- Identity middleware has been moved to `actix-identity` crate instead of @@ -161,7 +161,7 @@ ## 1.0.0 -* Extractor configuration. In version 1.0 this is handled with the new `Data` mechanism for both setting and retrieving the configuration +- Extractor configuration. In version 1.0 this is handled with the new `Data` mechanism for both setting and retrieving the configuration instead of @@ -219,7 +219,7 @@ ) ``` -* Resource registration. 1.0 version uses generalized resource +- Resource registration. 1.0 version uses generalized resource registration via `.service()` method. instead of @@ -239,7 +239,7 @@ .route(web::post().to(post_handler)) ``` -* Scope registration. +- Scope registration. instead of @@ -263,7 +263,7 @@ ); ``` -* `.with()`, `.with_async()` registration methods have been renamed to `.to()` and `.to_async()`. +- `.with()`, `.with_async()` registration methods have been renamed to `.to()` and `.to_async()`. instead of @@ -277,7 +277,7 @@ App.new().service(web::resource("/welcome").to(welcome)) ``` -* Passing arguments to handler with extractors, multiple arguments are allowed +- Passing arguments to handler with extractors, multiple arguments are allowed instead of @@ -295,7 +295,7 @@ } ``` -* `.f()`, `.a()` and `.h()` handler registration methods have been removed. +- `.f()`, `.a()` and `.h()` handler registration methods have been removed. Use `.to()` for handlers and `.to_async()` for async handlers. Handler function must use extractors. @@ -311,7 +311,7 @@ App.new().service(web::resource("/welcome").to(welcome)) ``` -* `HttpRequest` does not provide access to request's payload stream. +- `HttpRequest` does not provide access to request's payload stream. instead of @@ -341,7 +341,7 @@ } ``` -* `State` is now `Data`. You register Data during the App initialization process +- `State` is now `Data`. You register Data during the App initialization process and then access it from handlers either using a Data extractor or using HttpRequest's api. @@ -377,7 +377,7 @@ ``` -* AsyncResponder is removed, use `.to_async()` registration method and `impl Future<>` as result type. +- AsyncResponder is removed, use `.to_async()` registration method and `impl Future<>` as result type. instead of @@ -393,7 +393,7 @@ .. simply omit AsyncResponder and the corresponding responder() finish method -* Middleware +- Middleware instead of @@ -410,7 +410,7 @@ .route("/index.html", web::get().to(index)); ``` -* `HttpRequest::body()`, `HttpRequest::urlencoded()`, `HttpRequest::json()`, `HttpRequest::multipart()` +- `HttpRequest::body()`, `HttpRequest::urlencoded()`, `HttpRequest::json()`, `HttpRequest::multipart()` method have been removed. Use `Bytes`, `String`, `Form`, `Json`, `Multipart` extractors instead. instead of @@ -432,9 +432,9 @@ } ``` -* `actix_web::server` module has been removed. To start http server use `actix_web::HttpServer` type +- `actix_web::server` module has been removed. To start http server use `actix_web::HttpServer` type -* StaticFiles and NamedFile have been moved to a separate crate. +- StaticFiles and NamedFile have been moved to a separate crate. instead of `use actix_web::fs::StaticFile` @@ -444,20 +444,20 @@ use `use actix_files::NamedFile` -* Multipart has been moved to a separate crate. +- Multipart has been moved to a separate crate. instead of `use actix_web::multipart::Multipart` use `use actix_multipart::Multipart` -* Response compression is not enabled by default. +- Response compression is not enabled by default. To enable, use `Compress` middleware, `App::new().wrap(Compress::default())`. -* Session middleware moved to actix-session crate +- Session middleware moved to actix-session crate -* Actors support have been moved to `actix-web-actors` crate +- Actors support have been moved to `actix-web-actors` crate -* Custom Error +- Custom Error Instead of error_response method alone, ResponseError now provides two methods: error_response and render_response respectively. Where, error_response creates the error response and render_response returns the error response to the caller. @@ -471,7 +471,7 @@ ## 0.7.15 -* The `' '` character is not percent decoded anymore before matching routes. If you need to use it in +- The `' '` character is not percent decoded anymore before matching routes. If you need to use it in your routes, you should use `%20`. instead of @@ -496,18 +496,18 @@ } ``` -* If you used `AsyncResult::async` you need to replace it with `AsyncResult::future` +- If you used `AsyncResult::async` you need to replace it with `AsyncResult::future` ## 0.7.4 -* `Route::with_config()`/`Route::with_async_config()` always passes configuration objects as tuple +- `Route::with_config()`/`Route::with_async_config()` always passes configuration objects as tuple even for handler with one parameter. ## 0.7 -* `HttpRequest` does not implement `Stream` anymore. If you need to read request payload +- `HttpRequest` does not implement `Stream` anymore. If you need to read request payload use `HttpMessage::payload()` method. instead of @@ -533,10 +533,10 @@ } ``` -* [Middleware](https://actix.rs/actix-web/actix_web/middleware/trait.Middleware.html) +- [Middleware](https://actix.rs/actix-web/actix_web/middleware/trait.Middleware.html) trait uses `&HttpRequest` instead of `&mut HttpRequest`. -* Removed `Route::with2()` and `Route::with3()` use tuple of extractors instead. +- Removed `Route::with2()` and `Route::with3()` use tuple of extractors instead. instead of @@ -550,17 +550,17 @@ fn index((query, json): (Query<..>, Json impl Responder {} ``` -* `Handler::handle()` uses `&self` instead of `&mut self` +- `Handler::handle()` uses `&self` instead of `&mut self` -* `Handler::handle()` accepts reference to `HttpRequest<_>` instead of value +- `Handler::handle()` accepts reference to `HttpRequest<_>` instead of value -* Removed deprecated `HttpServer::threads()`, use +- Removed deprecated `HttpServer::threads()`, use [HttpServer::workers()](https://actix.rs/actix-web/actix_web/server/struct.HttpServer.html#method.workers) instead. -* Renamed `client::ClientConnectorError::Connector` to +- Renamed `client::ClientConnectorError::Connector` to `client::ClientConnectorError::Resolver` -* `Route::with()` does not return `ExtractorConfig`, to configure +- `Route::with()` does not return `ExtractorConfig`, to configure extractor use `Route::with_config()` instead of @@ -589,26 +589,26 @@ } ``` -* `Route::with_async()` does not return `ExtractorConfig`, to configure +- `Route::with_async()` does not return `ExtractorConfig`, to configure extractor use `Route::with_async_config()` ## 0.6 -* `Path` extractor return `ErrorNotFound` on failure instead of `ErrorBadRequest` +- `Path` extractor return `ErrorNotFound` on failure instead of `ErrorBadRequest` -* `ws::Message::Close` now includes optional close reason. +- `ws::Message::Close` now includes optional close reason. `ws::CloseCode::Status` and `ws::CloseCode::Empty` have been removed. -* `HttpServer::threads()` renamed to `HttpServer::workers()`. +- `HttpServer::threads()` renamed to `HttpServer::workers()`. -* `HttpServer::start_ssl()` and `HttpServer::start_tls()` deprecated. +- `HttpServer::start_ssl()` and `HttpServer::start_tls()` deprecated. Use `HttpServer::bind_ssl()` and `HttpServer::bind_tls()` instead. -* `HttpRequest::extensions()` returns read only reference to the request's Extension +- `HttpRequest::extensions()` returns read only reference to the request's Extension `HttpRequest::extensions_mut()` returns mutable reference. -* Instead of +- Instead of `use actix_web::middleware::{ CookieSessionBackend, CookieSessionError, RequestSession, @@ -619,15 +619,15 @@ `use actix_web::middleware::session{CookieSessionBackend, CookieSessionError, RequestSession, Session, SessionBackend, SessionImpl, SessionStorage};` -* `FromRequest::from_request()` accepts mutable reference to a request +- `FromRequest::from_request()` accepts mutable reference to a request -* `FromRequest::Result` has to implement `Into>` +- `FromRequest::Result` has to implement `Into>` -* [`Responder::respond_to()`]( +- [`Responder::respond_to()`]( https://actix.rs/actix-web/actix_web/trait.Responder.html#tymethod.respond_to) is generic over `S` -* Use `Query` extractor instead of HttpRequest::query()`. +- Use `Query` extractor instead of HttpRequest::query()`. ```rust fn index(q: Query>) -> Result<..> { @@ -641,37 +641,37 @@ let q = Query::>::extract(req); ``` -* Websocket operations are implemented as `WsWriter` trait. +- Websocket operations are implemented as `WsWriter` trait. you need to use `use actix_web::ws::WsWriter` ## 0.5 -* `HttpResponseBuilder::body()`, `.finish()`, `.json()` +- `HttpResponseBuilder::body()`, `.finish()`, `.json()` methods return `HttpResponse` instead of `Result` -* `actix_web::Method`, `actix_web::StatusCode`, `actix_web::Version` +- `actix_web::Method`, `actix_web::StatusCode`, `actix_web::Version` moved to `actix_web::http` module -* `actix_web::header` moved to `actix_web::http::header` +- `actix_web::header` moved to `actix_web::http::header` -* `NormalizePath` moved to `actix_web::http` module +- `NormalizePath` moved to `actix_web::http` module -* `HttpServer` moved to `actix_web::server`, added new `actix_web::server::new()` function, +- `HttpServer` moved to `actix_web::server`, added new `actix_web::server::new()` function, shortcut for `actix_web::server::HttpServer::new()` -* `DefaultHeaders` middleware does not use separate builder, all builder methods moved to type itself +- `DefaultHeaders` middleware does not use separate builder, all builder methods moved to type itself -* `StaticFiles::new()`'s show_index parameter removed, use `show_files_listing()` method instead. +- `StaticFiles::new()`'s show_index parameter removed, use `show_files_listing()` method instead. -* `CookieSessionBackendBuilder` removed, all methods moved to `CookieSessionBackend` type +- `CookieSessionBackendBuilder` removed, all methods moved to `CookieSessionBackend` type -* `actix_web::httpcodes` module is deprecated, `HttpResponse::Ok()`, `HttpResponse::Found()` and other `HttpResponse::XXX()` +- `actix_web::httpcodes` module is deprecated, `HttpResponse::Ok()`, `HttpResponse::Found()` and other `HttpResponse::XXX()` functions should be used instead -* `ClientRequestBuilder::body()` returns `Result<_, actix_web::Error>` +- `ClientRequestBuilder::body()` returns `Result<_, actix_web::Error>` instead of `Result<_, http::Error>` -* `Application` renamed to a `App` +- `Application` renamed to a `App` -* `actix_web::Reply`, `actix_web::Resource` moved to `actix_web::dev` +- `actix_web::Reply`, `actix_web::Resource` moved to `actix_web::dev` diff --git a/README.md b/README.md index 5cce9f3b9..a9afbf386 100644 --- a/README.md +++ b/README.md @@ -21,25 +21,25 @@ ## Features -* Supports *HTTP/1.x* and *HTTP/2* -* Streaming and pipelining -* Keep-alive and slow requests handling -* Client/server [WebSockets](https://actix.rs/docs/websockets/) support -* Transparent content compression/decompression (br, gzip, deflate, zstd) -* Powerful [request routing](https://actix.rs/docs/url-dispatch/) -* Multipart streams -* Static assets -* SSL support using OpenSSL or Rustls -* Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/)) -* Includes an async [HTTP client](https://docs.rs/awc/) -* Runs on stable Rust 1.52+ +- Supports *HTTP/1.x* and *HTTP/2* +- Streaming and pipelining +- Keep-alive and slow requests handling +- Client/server [WebSockets](https://actix.rs/docs/websockets/) support +- Transparent content compression/decompression (br, gzip, deflate, zstd) +- Powerful [request routing](https://actix.rs/docs/url-dispatch/) +- Multipart streams +- Static assets +- SSL support using OpenSSL or Rustls +- Middlewares ([Logger, Session, CORS, etc](https://actix.rs/docs/middleware/)) +- Includes an async [HTTP client](https://docs.rs/awc/) +- Runs on stable Rust 1.52+ ## Documentation -* [Website & User Guide](https://actix.rs) -* [Examples Repository](https://github.com/actix/examples) -* [API Documentation](https://docs.rs/actix-web) -* [API Documentation (master branch)](https://actix.rs/actix-web/actix_web) +- [Website & User Guide](https://actix.rs) +- [Examples Repository](https://github.com/actix/examples) +- [API Documentation](https://docs.rs/actix-web) +- [API Documentation (master branch)](https://actix.rs/actix-web/actix_web) ## Example @@ -71,18 +71,18 @@ async fn main() -> std::io::Result<()> { ### More examples -* [Basic Setup](https://github.com/actix/examples/tree/master/basics/basics/) -* [Application State](https://github.com/actix/examples/tree/master/basics/state/) -* [JSON Handling](https://github.com/actix/examples/tree/master/json/json/) -* [Multipart Streams](https://github.com/actix/examples/tree/master/forms/multipart/) -* [Diesel Integration](https://github.com/actix/examples/tree/master/database_interactions/diesel/) -* [r2d2 Integration](https://github.com/actix/examples/tree/master/database_interactions/r2d2/) -* [Simple WebSocket](https://github.com/actix/examples/tree/master/websockets/websocket/) -* [Tera Templates](https://github.com/actix/examples/tree/master/template_engines/tera/) -* [Askama Templates](https://github.com/actix/examples/tree/master/template_engines/askama/) -* [HTTPS using Rustls](https://github.com/actix/examples/tree/master/security/rustls/) -* [HTTPS using OpenSSL](https://github.com/actix/examples/tree/master/security/openssl/) -* [WebSocket Chat](https://github.com/actix/examples/tree/master/websockets/chat/) +- [Basic Setup](https://github.com/actix/examples/tree/master/basics/basics/) +- [Application State](https://github.com/actix/examples/tree/master/basics/state/) +- [JSON Handling](https://github.com/actix/examples/tree/master/json/json/) +- [Multipart Streams](https://github.com/actix/examples/tree/master/forms/multipart/) +- [Diesel Integration](https://github.com/actix/examples/tree/master/database_interactions/diesel/) +- [r2d2 Integration](https://github.com/actix/examples/tree/master/database_interactions/r2d2/) +- [Simple WebSocket](https://github.com/actix/examples/tree/master/websockets/websocket/) +- [Tera Templates](https://github.com/actix/examples/tree/master/template_engines/tera/) +- [Askama Templates](https://github.com/actix/examples/tree/master/template_engines/askama/) +- [HTTPS using Rustls](https://github.com/actix/examples/tree/master/security/rustls/) +- [HTTPS using OpenSSL](https://github.com/actix/examples/tree/master/security/openssl/) +- [WebSocket Chat](https://github.com/actix/examples/tree/master/websockets/chat/) You may consider checking out [this directory](https://github.com/actix/examples/tree/master/) for more examples. @@ -96,9 +96,9 @@ One of the fastest web frameworks available according to the This project is licensed under either of -* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or +- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0]) -* MIT license ([LICENSE-MIT](LICENSE-MIT) or +- MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT]) at your option. diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index d6b39e28f..ef8eba0fc 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -4,42 +4,42 @@ ## 0.6.0-beta.10 - 2021-12-11 -* No significant changes since `0.6.0-beta.9`. +- No significant changes since `0.6.0-beta.9`. ## 0.6.0-beta.9 - 2021-11-22 -* Add crate feature `experimental-io-uring`, enabling async file I/O to be utilized. This feature is only available on Linux OSes with recent kernel versions. This feature is semver-exempt. [#2408] -* Add `NamedFile::open_async`. [#2408] -* Fix 304 Not Modified responses to omit the Content-Length header, as per the spec. [#2453] -* The `Responder` impl for `NamedFile` now has a boxed future associated type. [#2408] -* The `Service` impl for `NamedFileService` now has a boxed future associated type. [#2408] -* Add `impl Clone` for `FilesService`. [#2408] +- Add crate feature `experimental-io-uring`, enabling async file I/O to be utilized. This feature is only available on Linux OSes with recent kernel versions. This feature is semver-exempt. [#2408] +- Add `NamedFile::open_async`. [#2408] +- Fix 304 Not Modified responses to omit the Content-Length header, as per the spec. [#2453] +- The `Responder` impl for `NamedFile` now has a boxed future associated type. [#2408] +- The `Service` impl for `NamedFileService` now has a boxed future associated type. [#2408] +- Add `impl Clone` for `FilesService`. [#2408] [#2408]: https://github.com/actix/actix-web/pull/2408 [#2453]: https://github.com/actix/actix-web/pull/2453 ## 0.6.0-beta.8 - 2021-10-20 -* Minimum supported Rust version (MSRV) is now 1.52. +- Minimum supported Rust version (MSRV) is now 1.52. ## 0.6.0-beta.7 - 2021-09-09 -* Minimum supported Rust version (MSRV) is now 1.51. +- Minimum supported Rust version (MSRV) is now 1.51. ## 0.6.0-beta.6 - 2021-06-26 -* Added `Files::path_filter()`. [#2274] -* `Files::show_files_listing()` can now be used with `Files::index_file()` to show files listing as a fallback when the index file is not found. [#2228] +- Added `Files::path_filter()`. [#2274] +- `Files::show_files_listing()` can now be used with `Files::index_file()` to show files listing as a fallback when the index file is not found. [#2228] [#2274]: https://github.com/actix/actix-web/pull/2274 [#2228]: https://github.com/actix/actix-web/pull/2228 ## 0.6.0-beta.5 - 2021-06-17 -* `NamedFile` now implements `ServiceFactory` and `HttpServiceFactory` making it much more useful in routing. For example, it can be used directly as a default service. [#2135] -* For symbolic links, `Content-Disposition` header no longer shows the filename of the original file. [#2156] -* `Files::redirect_to_slash_directory()` now works as expected when used with `Files::show_files_listing()`. [#2225] -* `application/{javascript, json, wasm}` mime type now have `inline` disposition by default. [#2257] +- `NamedFile` now implements `ServiceFactory` and `HttpServiceFactory` making it much more useful in routing. For example, it can be used directly as a default service. [#2135] +- For symbolic links, `Content-Disposition` header no longer shows the filename of the original file. [#2156] +- `Files::redirect_to_slash_directory()` now works as expected when used with `Files::show_files_listing()`. [#2225] +- `application/{javascript, json, wasm}` mime type now have `inline` disposition by default. [#2257] [#2135]: https://github.com/actix/actix-web/pull/2135 [#2156]: https://github.com/actix/actix-web/pull/2156 @@ -48,130 +48,130 @@ ## 0.6.0-beta.4 - 2021-04-02 -* Add support for `.guard` in `Files` to selectively filter `Files` services. [#2046] +- Add support for `.guard` in `Files` to selectively filter `Files` services. [#2046] [#2046]: https://github.com/actix/actix-web/pull/2046 ## 0.6.0-beta.3 - 2021-03-09 -* No notable changes. +- No notable changes. ## 0.6.0-beta.2 - 2021-02-10 -* Fix If-Modified-Since and If-Unmodified-Since to not compare using sub-second timestamps. [#1887] -* Replace `v_htmlescape` with `askama_escape`. [#1953] +- Fix If-Modified-Since and If-Unmodified-Since to not compare using sub-second timestamps. [#1887] +- Replace `v_htmlescape` with `askama_escape`. [#1953] [#1887]: https://github.com/actix/actix-web/pull/1887 [#1953]: https://github.com/actix/actix-web/pull/1953 ## 0.6.0-beta.1 - 2021-01-07 -* `HttpRange::parse` now has its own error type. -* Update `bytes` to `1.0`. [#1813] +- `HttpRange::parse` now has its own error type. +- Update `bytes` to `1.0`. [#1813] [#1813]: https://github.com/actix/actix-web/pull/1813 ## 0.5.0 - 2020-12-26 -* Optionally support hidden files/directories. [#1811] +- Optionally support hidden files/directories. [#1811] [#1811]: https://github.com/actix/actix-web/pull/1811 ## 0.4.1 - 2020-11-24 -* Clarify order of parameters in `Files::new` and improve docs. +- Clarify order of parameters in `Files::new` and improve docs. ## 0.4.0 - 2020-10-06 -* Add `Files::prefer_utf8` option that adds UTF-8 charset on certain response types. [#1714] +- Add `Files::prefer_utf8` option that adds UTF-8 charset on certain response types. [#1714] [#1714]: https://github.com/actix/actix-web/pull/1714 ## 0.3.0 - 2020-09-11 -* No significant changes from 0.3.0-beta.1. +- No significant changes from 0.3.0-beta.1. ## 0.3.0-beta.1 - 2020-07-15 -* Update `v_htmlescape` to 0.10 -* Update `actix-web` and `actix-http` dependencies to beta.1 +- Update `v_htmlescape` to 0.10 +- Update `actix-web` and `actix-http` dependencies to beta.1 ## 0.3.0-alpha.1 - 2020-05-23 -* Update `actix-web` and `actix-http` dependencies to alpha -* Fix some typos in the docs -* Bump minimum supported Rust version to 1.40 -* Support sending Content-Length when Content-Range is specified [#1384] +- Update `actix-web` and `actix-http` dependencies to alpha +- Fix some typos in the docs +- Bump minimum supported Rust version to 1.40 +- Support sending Content-Length when Content-Range is specified [#1384] [#1384]: https://github.com/actix/actix-web/pull/1384 ## 0.2.1 - 2019-12-22 -* Use the same format for file URLs regardless of platforms +- Use the same format for file URLs regardless of platforms ## 0.2.0 - 2019-12-20 -* Fix BodyEncoding trait import #1220 +- Fix BodyEncoding trait import #1220 ## 0.2.0-alpha.1 - 2019-12-07 -* Migrate to `std::future` +- Migrate to `std::future` ## 0.1.7 - 2019-11-06 -* Add an additional `filename*` param in the `Content-Disposition` header of +- Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151) ## 0.1.6 - 2019-10-14 -* Add option to redirect to a slash-ended path `Files` #1132 +- Add option to redirect to a slash-ended path `Files` #1132 ## 0.1.5 - 2019-10-08 -* Bump up `mime_guess` crate version to 2.0.1 -* Bump up `percent-encoding` crate version to 2.1 -* Allow user defined request guards for `Files` #1113 +- Bump up `mime_guess` crate version to 2.0.1 +- Bump up `percent-encoding` crate version to 2.1 +- Allow user defined request guards for `Files` #1113 ## 0.1.4 - 2019-07-20 -* Allow to disable `Content-Disposition` header #686 +- Allow to disable `Content-Disposition` header #686 ## 0.1.3 - 2019-06-28 -* Do not set `Content-Length` header, let actix-http set it #930 +- Do not set `Content-Length` header, let actix-http set it #930 ## 0.1.2 - 2019-06-13 -* Content-Length is 0 for NamedFile HEAD request #914 -* Fix ring dependency from actix-web default features for #741 +- Content-Length is 0 for NamedFile HEAD request #914 +- Fix ring dependency from actix-web default features for #741 ## 0.1.1 - 2019-06-01 -* Static files are incorrectly served as both chunked and with length #812 +- Static files are incorrectly served as both chunked and with length #812 ## 0.1.0 - 2019-05-25 -* NamedFile last-modified check always fails due to nano-seconds in file modified date #820 +- NamedFile last-modified check always fails due to nano-seconds in file modified date #820 ## 0.1.0-beta.4 - 2019-05-12 -* Update actix-web to beta.4 +- Update actix-web to beta.4 ## 0.1.0-beta.1 - 2019-04-20 -* Update actix-web to beta.1 +- Update actix-web to beta.1 ## 0.1.0-alpha.6 - 2019-04-14 -* Update actix-web to alpha6 +- Update actix-web to alpha6 ## 0.1.0-alpha.4 - 2019-04-08 -* Update actix-web to alpha4 +- Update actix-web to alpha4 ## 0.1.0-alpha.2 - 2019-04-02 -* Add default handler support +- Add default handler support ## 0.1.0-alpha.1 - 2019-03-28 -* Initial impl +- Initial impl diff --git a/actix-http-test/CHANGES.md b/actix-http-test/CHANGES.md index 156012168..4e86e20e8 100644 --- a/actix-http-test/CHANGES.md +++ b/actix-http-test/CHANGES.md @@ -4,125 +4,125 @@ ## 3.0.0-beta.9 - 2021-12-11 -* No significant changes since `3.0.0-beta.8`. +- No significant changes since `3.0.0-beta.8`. ## 3.0.0-beta.8 - 2021-11-30 -* Update `actix-tls` to `3.0.0-rc.1`. [#2474] +- Update `actix-tls` to `3.0.0-rc.1`. [#2474] [#2474]: https://github.com/actix/actix-web/pull/2474 ## 3.0.0-beta.7 - 2021-11-22 -* Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408] +- Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408] [#2408]: https://github.com/actix/actix-web/pull/2408 ## 3.0.0-beta.6 - 2021-11-15 -* `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442] -* Update `actix-server` to `2.0.0-beta.9`. [#2442] -* Minimum supported Rust version (MSRV) is now 1.52. +- `TestServer::stop` is now async and will wait for the server and system to shutdown. [#2442] +- Update `actix-server` to `2.0.0-beta.9`. [#2442] +- Minimum supported Rust version (MSRV) is now 1.52. [#2442]: https://github.com/actix/actix-web/pull/2442 ## 3.0.0-beta.5 - 2021-09-09 -* Minimum supported Rust version (MSRV) is now 1.51. +- Minimum supported Rust version (MSRV) is now 1.51. ## 3.0.0-beta.4 - 2021-04-02 -* Added `TestServer::client_headers` method. [#2097] +- Added `TestServer::client_headers` method. [#2097] [#2097]: https://github.com/actix/actix-web/pull/2097 ## 3.0.0-beta.3 - 2021-03-09 -* No notable changes. +- No notable changes. ## 3.0.0-beta.2 - 2021-02-10 -* No notable changes. +- No notable changes. ## 3.0.0-beta.1 - 2021-01-07 -* Update `bytes` to `1.0`. [#1813] +- Update `bytes` to `1.0`. [#1813] [#1813]: https://github.com/actix/actix-web/pull/1813 ## 2.1.0 - 2020-11-25 -* Add ability to set address for `TestServer`. [#1645] -* Upgrade `base64` to `0.13`. -* Upgrade `serde_urlencoded` to `0.7`. [#1773] +- Add ability to set address for `TestServer`. [#1645] +- Upgrade `base64` to `0.13`. +- Upgrade `serde_urlencoded` to `0.7`. [#1773] [#1773]: https://github.com/actix/actix-web/pull/1773 [#1645]: https://github.com/actix/actix-web/pull/1645 ## 2.0.0 - 2020-09-11 -* Update actix-codec and actix-utils dependencies. +- Update actix-codec and actix-utils dependencies. ## 2.0.0-alpha.1 - 2020-05-23 -* Update the `time` dependency to 0.2.7 -* Update `actix-connect` dependency to 2.0.0-alpha.2 -* Make `test_server` `async` fn. -* Bump minimum supported Rust version to 1.40 -* Replace deprecated `net2` crate with `socket2` -* Update `base64` dependency to 0.12 -* Update `env_logger` dependency to 0.7 +- Update the `time` dependency to 0.2.7 +- Update `actix-connect` dependency to 2.0.0-alpha.2 +- Make `test_server` `async` fn. +- Bump minimum supported Rust version to 1.40 +- Replace deprecated `net2` crate with `socket2` +- Update `base64` dependency to 0.12 +- Update `env_logger` dependency to 0.7 ## 1.0.0 - 2019-12-13 -* Replaced `TestServer::start()` with `test_server()` +- Replaced `TestServer::start()` with `test_server()` ## 1.0.0-alpha.3 - 2019-12-07 -* Migrate to `std::future` +- Migrate to `std::future` ## 0.2.5 - 2019-09-17 -* Update serde_urlencoded to "0.6.1" -* Increase TestServerRuntime timeouts from 500ms to 3000ms -* Do not override current `System` +- Update serde_urlencoded to "0.6.1" +- Increase TestServerRuntime timeouts from 500ms to 3000ms +- Do not override current `System` ## 0.2.4 - 2019-07-18 -* Update actix-server to 0.6 +- Update actix-server to 0.6 ## 0.2.3 - 2019-07-16 -* Add `delete`, `options`, `patch` methods to `TestServerRunner` +- Add `delete`, `options`, `patch` methods to `TestServerRunner` ## 0.2.2 - 2019-06-16 -* Add .put() and .sput() methods +- Add .put() and .sput() methods ## 0.2.1 - 2019-06-05 -* Add license files +- Add license files ## 0.2.0 - 2019-05-12 -* Update awc and actix-http deps +- Update awc and actix-http deps ## 0.1.1 - 2019-04-24 -* Always make new connection for http client +- Always make new connection for http client ## 0.1.0 - 2019-04-16 -* No changes +- No changes ## 0.1.0-alpha.3 - 2019-04-02 -* Request functions accept path #743 +- Request functions accept path #743 ## 0.1.0-alpha.2 - 2019-03-29 -* Added TestServerRuntime::load_body() method -* Update actix-http and awc libraries +- Added TestServerRuntime::load_body() method +- Update actix-http and awc libraries ## 0.1.0-alpha.1 - 2019-03-28 -* Initial impl +- Initial impl diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index ad98d132a..3b45e934f 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -2,22 +2,22 @@ ## Unreleased - 2021-xx-xx ### Changes -* `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527] +- `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527] [#2527]: https://github.com/actix/actix-web/pull/2527 ## 3.0.0-beta.16 - 2021-12-17 ### Added -* New method on `MessageBody` trait, `try_into_bytes`, with default implementation, for optimizations on body types that complete in exactly one poll. Replaces `is_complete_body` and `take_complete_body`. [#2522] +- New method on `MessageBody` trait, `try_into_bytes`, with default implementation, for optimizations on body types that complete in exactly one poll. Replaces `is_complete_body` and `take_complete_body`. [#2522] ### Changed -* Rename trait `IntoHeaderPair => TryIntoHeaderPair`. [#2510] -* Rename `TryIntoHeaderPair::{try_into_header_pair => try_into_pair}`. [#2510] -* Rename trait `IntoHeaderValue => TryIntoHeaderValue`. [#2510] +- Rename trait `IntoHeaderPair => TryIntoHeaderPair`. [#2510] +- Rename `TryIntoHeaderPair::{try_into_header_pair => try_into_pair}`. [#2510] +- Rename trait `IntoHeaderValue => TryIntoHeaderValue`. [#2510] ### Removed -* `MessageBody::{is_complete_body,take_complete_body}`. [#2522] +- `MessageBody::{is_complete_body,take_complete_body}`. [#2522] [#2510]: https://github.com/actix/actix-web/pull/2510 [#2522]: https://github.com/actix/actix-web/pull/2522 @@ -25,43 +25,43 @@ ## 3.0.0-beta.15 - 2021-12-11 ### Added -* Add timeout for canceling HTTP/2 server side connection handshake. Default to 5 seconds. [#2483] -* HTTP/2 handshake timeout can be configured with `ServiceConfig::client_timeout`. [#2483] -* `Response::map_into_boxed_body`. [#2468] -* `body::EitherBody` enum. [#2468] -* `body::None` struct. [#2468] -* Impl `MessageBody` for `bytestring::ByteString`. [#2468] -* `impl Clone for ws::HandshakeError`. [#2468] -* `#[must_use]` for `ws::Codec` to prevent subtle bugs. [#1920] -* `impl Default ` for `ws::Codec`. [#1920] -* `header::QualityItem::{max, min}`. [#2486] -* `header::Quality::{MAX, MIN}`. [#2486] -* `impl Display` for `header::Quality`. [#2486] -* Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491] -* `Request::take_conn_data()`. [#2491] -* `Request::take_req_data()`. [#2487] -* `impl Clone` for `RequestHead`. [#2487] -* New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimizations on body types that are done in exactly one poll/chunk. [#2497] -* New `boxed` method on `MessageBody` trait for wrapping body type. [#2520] +- Add timeout for canceling HTTP/2 server side connection handshake. Default to 5 seconds. [#2483] +- HTTP/2 handshake timeout can be configured with `ServiceConfig::client_timeout`. [#2483] +- `Response::map_into_boxed_body`. [#2468] +- `body::EitherBody` enum. [#2468] +- `body::None` struct. [#2468] +- Impl `MessageBody` for `bytestring::ByteString`. [#2468] +- `impl Clone for ws::HandshakeError`. [#2468] +- `#[must_use]` for `ws::Codec` to prevent subtle bugs. [#1920] +- `impl Default ` for `ws::Codec`. [#1920] +- `header::QualityItem::{max, min}`. [#2486] +- `header::Quality::{MAX, MIN}`. [#2486] +- `impl Display` for `header::Quality`. [#2486] +- Connection data set through the `on_connect_ext` callbacks is now accessible only from the new `Request::conn_data()` method. [#2491] +- `Request::take_conn_data()`. [#2491] +- `Request::take_req_data()`. [#2487] +- `impl Clone` for `RequestHead`. [#2487] +- New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimizations on body types that are done in exactly one poll/chunk. [#2497] +- New `boxed` method on `MessageBody` trait for wrapping body type. [#2520] ### Changed -* Rename `body::BoxBody::{from_body => new}`. [#2468] -* Body type for `Responses` returned from `Response::{new, ok, etc...}` is now `BoxBody`. [#2468] -* The `Error` associated type on `MessageBody` type now requires `impl Error` (or similar). [#2468] -* Error types using in service builders now require `Into>`. [#2468] -* `From` implementations on error types now return a `Response`. [#2468] -* `ResponseBuilder::body(B)` now returns `Response>`. [#2468] -* `ResponseBuilder::finish()` now returns `Response>`. [#2468] +- Rename `body::BoxBody::{from_body => new}`. [#2468] +- Body type for `Responses` returned from `Response::{new, ok, etc...}` is now `BoxBody`. [#2468] +- The `Error` associated type on `MessageBody` type now requires `impl Error` (or similar). [#2468] +- Error types using in service builders now require `Into>`. [#2468] +- `From` implementations on error types now return a `Response`. [#2468] +- `ResponseBuilder::body(B)` now returns `Response>`. [#2468] +- `ResponseBuilder::finish()` now returns `Response>`. [#2468] ### Removed -* `ResponseBuilder::streaming`. [#2468] -* `impl Future` for `ResponseBuilder`. [#2468] -* Remove unnecessary `MessageBody` bound on types passed to `body::AnyBody::new`. [#2468] -* Move `body::AnyBody` to `awc`. Replaced with `EitherBody` and `BoxBody`. [#2468] -* `impl Copy` for `ws::Codec`. [#1920] -* `header::qitem` helper. Replaced with `header::QualityItem::max`. [#2486] -* `impl TryFrom` for `header::Quality`. [#2486] -* `http` module. Most everything it contained is exported at the crate root. [#2488] +- `ResponseBuilder::streaming`. [#2468] +- `impl Future` for `ResponseBuilder`. [#2468] +- Remove unnecessary `MessageBody` bound on types passed to `body::AnyBody::new`. [#2468] +- Move `body::AnyBody` to `awc`. Replaced with `EitherBody` and `BoxBody`. [#2468] +- `impl Copy` for `ws::Codec`. [#1920] +- `header::qitem` helper. Replaced with `header::QualityItem::max`. [#2486] +- `impl TryFrom` for `header::Quality`. [#2486] +- `http` module. Most everything it contained is exported at the crate root. [#2488] [#2483]: https://github.com/actix/actix-web/pull/2483 [#2468]: https://github.com/actix/actix-web/pull/2468 @@ -76,10 +76,10 @@ ## 3.0.0-beta.14 - 2021-11-30 ### Changed -* Guarantee ordering of `header::GetAll` iterator to be same as insertion order. [#2467] -* Expose `header::map` module. [#2467] -* Implement `ExactSizeIterator` and `FusedIterator` for all `HeaderMap` iterators. [#2470] -* Update `actix-tls` to `3.0.0-rc.1`. [#2474] +- Guarantee ordering of `header::GetAll` iterator to be same as insertion order. [#2467] +- Expose `header::map` module. [#2467] +- Implement `ExactSizeIterator` and `FusedIterator` for all `HeaderMap` iterators. [#2470] +- Update `actix-tls` to `3.0.0-rc.1`. [#2474] [#2467]: https://github.com/actix/actix-web/pull/2467 [#2470]: https://github.com/actix/actix-web/pull/2470 @@ -88,24 +88,24 @@ ## 3.0.0-beta.13 - 2021-11-22 ### Added -* `body::AnyBody::empty` for quickly creating an empty body. [#2446] -* `body::AnyBody::none` for quickly creating a "none" body. [#2456] -* `impl Clone` for `body::AnyBody where S: Clone`. [#2448] -* `body::AnyBody::into_boxed` for quickly converting to a type-erased, boxed body type. [#2448] +- `body::AnyBody::empty` for quickly creating an empty body. [#2446] +- `body::AnyBody::none` for quickly creating a "none" body. [#2456] +- `impl Clone` for `body::AnyBody where S: Clone`. [#2448] +- `body::AnyBody::into_boxed` for quickly converting to a type-erased, boxed body type. [#2448] ### Changed -* Rename `body::AnyBody::{Message => Body}`. [#2446] -* Rename `body::AnyBody::{from_message => new_boxed}`. [#2448] -* Rename `body::AnyBody::{from_slice => copy_from_slice}`. [#2448] -* Rename `body::{BoxAnyBody => BoxBody}`. [#2448] -* Change representation of `AnyBody` to include a type parameter in `Body` variant. Defaults to `BoxBody`. [#2448] -* `Encoder::response` now returns `AnyBody>`. [#2448] +- Rename `body::AnyBody::{Message => Body}`. [#2446] +- Rename `body::AnyBody::{from_message => new_boxed}`. [#2448] +- Rename `body::AnyBody::{from_slice => copy_from_slice}`. [#2448] +- Rename `body::{BoxAnyBody => BoxBody}`. [#2448] +- Change representation of `AnyBody` to include a type parameter in `Body` variant. Defaults to `BoxBody`. [#2448] +- `Encoder::response` now returns `AnyBody>`. [#2448] ### Removed -* `body::AnyBody::Empty`; an empty body can now only be represented as a zero-length `Bytes` variant. [#2446] -* `body::BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446] -* `EncoderError::Boxed`; it is no longer required. [#2446] -* `body::ResponseBody`; is function is replaced by the new `body::AnyBody` enum. [#2446] +- `body::AnyBody::Empty`; an empty body can now only be represented as a zero-length `Bytes` variant. [#2446] +- `body::BodySize::Empty`; an empty body can now only be represented as a `Sized(0)` variant. [#2446] +- `EncoderError::Boxed`; it is no longer required. [#2446] +- `body::ResponseBody`; is function is replaced by the new `body::AnyBody` enum. [#2446] [#2446]: https://github.com/actix/actix-web/pull/2446 [#2448]: https://github.com/actix/actix-web/pull/2448 @@ -114,11 +114,11 @@ ## 3.0.0-beta.12 - 2021-11-15 ### Changed -* Update `actix-server` to `2.0.0-beta.9`. [#2442] +- Update `actix-server` to `2.0.0-beta.9`. [#2442] ### Removed -* `client` module. [#2425] -* `trust-dns` feature. [#2425] +- `client` module. [#2425] +- `trust-dns` feature. [#2425] [#2425]: https://github.com/actix/actix-web/pull/2425 [#2442]: https://github.com/actix/actix-web/pull/2442 @@ -126,21 +126,21 @@ ## 3.0.0-beta.11 - 2021-10-20 ### Changed -* Updated rustls to v0.20. [#2414] -* Minimum supported Rust version (MSRV) is now 1.52. +- Updated rustls to v0.20. [#2414] +- Minimum supported Rust version (MSRV) is now 1.52. [#2414]: https://github.com/actix/actix-web/pull/2414 ## 3.0.0-beta.10 - 2021-09-09 ### Changed -* `ContentEncoding` is now marked `#[non_exhaustive]`. [#2377] -* Minimum supported Rust version (MSRV) is now 1.51. +- `ContentEncoding` is now marked `#[non_exhaustive]`. [#2377] +- Minimum supported Rust version (MSRV) is now 1.51. ### Fixed -* Remove slice creation pointing to potential uninitialized data on h1 encoder. [#2364] -* Remove `Into` bound on `Encoder` body types. [#2375] -* Fix quality parse error in Accept-Encoding header. [#2344] +- Remove slice creation pointing to potential uninitialized data on h1 encoder. [#2364] +- Remove `Into` bound on `Encoder` body types. [#2375] +- Fix quality parse error in Accept-Encoding header. [#2344] [#2364]: https://github.com/actix/actix-web/pull/2364 [#2375]: https://github.com/actix/actix-web/pull/2375 @@ -150,15 +150,15 @@ ## 3.0.0-beta.9 - 2021-08-09 ### Fixed -* Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977) +- Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977) ## 3.0.0-beta.8 - 2021-06-26 ### Changed -* Change compression algorithm features flags. [#2250] +- Change compression algorithm features flags. [#2250] ### Removed -* `downcast` and `downcast_get_type_id` macros. [#2291] +- `downcast` and `downcast_get_type_id` macros. [#2291] [#2291]: https://github.com/actix/actix-web/pull/2291 [#2250]: https://github.com/actix/actix-web/pull/2250 @@ -166,37 +166,37 @@ ## 3.0.0-beta.7 - 2021-06-17 ### Added -* Alias `body::Body` as `body::AnyBody`. [#2215] -* `BoxAnyBody`: a boxed message body with boxed errors. [#2183] -* Re-export `http` crate's `Error` type as `error::HttpError`. [#2171] -* Re-export `StatusCode`, `Method`, `Version` and `Uri` at the crate root. [#2171] -* Re-export `ContentEncoding` and `ConnectionType` at the crate root. [#2171] -* `Response::into_body` that consumes response and returns body type. [#2201] -* `impl Default` for `Response`. [#2201] -* Add zstd support for `ContentEncoding`. [#2244] +- Alias `body::Body` as `body::AnyBody`. [#2215] +- `BoxAnyBody`: a boxed message body with boxed errors. [#2183] +- Re-export `http` crate's `Error` type as `error::HttpError`. [#2171] +- Re-export `StatusCode`, `Method`, `Version` and `Uri` at the crate root. [#2171] +- Re-export `ContentEncoding` and `ConnectionType` at the crate root. [#2171] +- `Response::into_body` that consumes response and returns body type. [#2201] +- `impl Default` for `Response`. [#2201] +- Add zstd support for `ContentEncoding`. [#2244] ### Changed -* The `MessageBody` trait now has an associated `Error` type. [#2183] -* All error trait bounds in server service builders have changed from `Into` to `Into>`. [#2253] -* All error trait bounds in message body and stream impls changed from `Into` to `Into>`. [#2253] -* Places in `Response` where `ResponseBody` was received or returned now simply use `B`. [#2201] -* `header` mod is now public. [#2171] -* `uri` mod is now public. [#2171] -* Update `language-tags` to `0.3`. -* Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201] -* `ResponseBuilder::message_body` now returns a `Result`. [#2201] -* Remove `Unpin` bound on `ResponseBuilder::streaming`. [#2253] -* `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226] +- The `MessageBody` trait now has an associated `Error` type. [#2183] +- All error trait bounds in server service builders have changed from `Into` to `Into>`. [#2253] +- All error trait bounds in message body and stream impls changed from `Into` to `Into>`. [#2253] +- Places in `Response` where `ResponseBody` was received or returned now simply use `B`. [#2201] +- `header` mod is now public. [#2171] +- `uri` mod is now public. [#2171] +- Update `language-tags` to `0.3`. +- Reduce the level from `error` to `debug` for the log line that is emitted when a `500 Internal Server Error` is built using `HttpResponse::from_error`. [#2201] +- `ResponseBuilder::message_body` now returns a `Result`. [#2201] +- Remove `Unpin` bound on `ResponseBuilder::streaming`. [#2253] +- `HttpServer::{listen_rustls(), bind_rustls()}` now honor the ALPN protocols in the configuation parameter. [#2226] ### Removed -* Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171] -* Down-casting for `MessageBody` types. [#2183] -* `error::Result` alias. [#2201] -* Error field from `Response` and `Response::error`. [#2205] -* `impl Future` for `Response`. [#2201] -* `Response::take_body` and old `Response::into_body` method that casted body type. [#2201] -* `InternalError` and all the error types it constructed. [#2215] -* Conversion (`impl Into`) of `Response` and `ResponseBuilder` to `Error`. [#2215] +- Stop re-exporting `http` crate's `HeaderMap` types in addition to ours. [#2171] +- Down-casting for `MessageBody` types. [#2183] +- `error::Result` alias. [#2201] +- Error field from `Response` and `Response::error`. [#2205] +- `impl Future` for `Response`. [#2201] +- `Response::take_body` and old `Response::into_body` method that casted body type. [#2201] +- `InternalError` and all the error types it constructed. [#2215] +- Conversion (`impl Into`) of `Response` and `ResponseBuilder` to `Error`. [#2215] [#2171]: https://github.com/actix/actix-web/pull/2171 [#2183]: https://github.com/actix/actix-web/pull/2183 @@ -211,27 +211,27 @@ ## 3.0.0-beta.6 - 2021-04-17 ### Added -* `impl MessageBody for Pin>`. [#2152] -* `Response::{ok, bad_request, not_found, internal_server_error}`. [#2159] -* Helper `body::to_bytes` for async collecting message body into Bytes. [#2158] +- `impl MessageBody for Pin>`. [#2152] +- `Response::{ok, bad_request, not_found, internal_server_error}`. [#2159] +- Helper `body::to_bytes` for async collecting message body into Bytes. [#2158] ### Changes -* The type parameter of `Response` no longer has a default. [#2152] -* The `Message` variant of `body::Body` is now `Pin>`. [#2152] -* `BodyStream` and `SizedStream` are no longer restricted to Unpin types. [#2152] -* Error enum types are marked `#[non_exhaustive]`. [#2161] +- The type parameter of `Response` no longer has a default. [#2152] +- The `Message` variant of `body::Body` is now `Pin>`. [#2152] +- `BodyStream` and `SizedStream` are no longer restricted to Unpin types. [#2152] +- Error enum types are marked `#[non_exhaustive]`. [#2161] ### Removed -* `cookies` feature flag. [#2065] -* Top-level `cookies` mod (re-export). [#2065] -* `HttpMessage` trait loses the `cookies` and `cookie` methods. [#2065] -* `impl ResponseError for CookieParseError`. [#2065] -* Deprecated methods on `ResponseBuilder`: `if_true`, `if_some`. [#2148] -* `ResponseBuilder::json`. [#2148] -* `ResponseBuilder::{set_header, header}`. [#2148] -* `impl From for Body`. [#2148] -* `Response::build_from`. [#2159] -* Most of the status code builders on `Response`. [#2159] +- `cookies` feature flag. [#2065] +- Top-level `cookies` mod (re-export). [#2065] +- `HttpMessage` trait loses the `cookies` and `cookie` methods. [#2065] +- `impl ResponseError for CookieParseError`. [#2065] +- Deprecated methods on `ResponseBuilder`: `if_true`, `if_some`. [#2148] +- `ResponseBuilder::json`. [#2148] +- `ResponseBuilder::{set_header, header}`. [#2148] +- `impl From for Body`. [#2148] +- `Response::build_from`. [#2159] +- Most of the status code builders on `Response`. [#2159] [#2065]: https://github.com/actix/actix-web/pull/2065 [#2148]: https://github.com/actix/actix-web/pull/2148 @@ -243,16 +243,16 @@ ## 3.0.0-beta.5 - 2021-04-02 ### Added -* `client::Connector::handshake_timeout` method for customizing TLS connection handshake timeout. [#2081] -* `client::ConnectorService` as `client::Connector::finish` method's return type [#2081] -* `client::ConnectionIo` trait alias [#2081] +- `client::Connector::handshake_timeout` method for customizing TLS connection handshake timeout. [#2081] +- `client::ConnectorService` as `client::Connector::finish` method's return type [#2081] +- `client::ConnectionIo` trait alias [#2081] ### Changed -* `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063] +- `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063] ### Removed -* Common typed HTTP headers were moved to actix-web. [2094] -* `ResponseError` impl for `actix_utils::timeout::TimeoutError`. [#2127] +- Common typed HTTP headers were moved to actix-web. [2094] +- `ResponseError` impl for `actix_utils::timeout::TimeoutError`. [#2127] [#2063]: https://github.com/actix/actix-web/pull/2063 [#2081]: https://github.com/actix/actix-web/pull/2081 @@ -262,13 +262,13 @@ ## 3.0.0-beta.4 - 2021-03-08 ### Changed -* Feature `cookies` is now optional and disabled by default. [#1981] -* `ws::hash_key` now returns array. [#2035] -* `ResponseBuilder::json` now takes `impl Serialize`. [#2052] +- Feature `cookies` is now optional and disabled by default. [#1981] +- `ws::hash_key` now returns array. [#2035] +- `ResponseBuilder::json` now takes `impl Serialize`. [#2052] ### Removed -* Re-export of `futures_channel::oneshot::Canceled` is removed from `error` mod. [#1994] -* `ResponseError` impl for `futures_channel::oneshot::Canceled` is removed. [#1994] +- Re-export of `futures_channel::oneshot::Canceled` is removed from `error` mod. [#1994] +- `ResponseError` impl for `futures_channel::oneshot::Canceled` is removed. [#1994] [#1981]: https://github.com/actix/actix-web/pull/1981 [#1994]: https://github.com/actix/actix-web/pull/1994 @@ -277,48 +277,48 @@ ## 3.0.0-beta.3 - 2021-02-10 -* No notable changes. +- No notable changes. ## 3.0.0-beta.2 - 2021-02-10 ### Added -* `TryIntoHeaderPair` trait that allows using typed and untyped headers in the same methods. [#1869] -* `ResponseBuilder::insert_header` method which allows using typed headers. [#1869] -* `ResponseBuilder::append_header` method which allows using typed headers. [#1869] -* `TestRequest::insert_header` method which allows using typed headers. [#1869] -* `ContentEncoding` implements all necessary header traits. [#1912] -* `HeaderMap::len_keys` has the behavior of the old `len` method. [#1964] -* `HeaderMap::drain` as an efficient draining iterator. [#1964] -* Implement `IntoIterator` for owned `HeaderMap`. [#1964] -* `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969] +- `TryIntoHeaderPair` trait that allows using typed and untyped headers in the same methods. [#1869] +- `ResponseBuilder::insert_header` method which allows using typed headers. [#1869] +- `ResponseBuilder::append_header` method which allows using typed headers. [#1869] +- `TestRequest::insert_header` method which allows using typed headers. [#1869] +- `ContentEncoding` implements all necessary header traits. [#1912] +- `HeaderMap::len_keys` has the behavior of the old `len` method. [#1964] +- `HeaderMap::drain` as an efficient draining iterator. [#1964] +- Implement `IntoIterator` for owned `HeaderMap`. [#1964] +- `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969] ### Changed -* `ResponseBuilder::content_type` now takes an `impl TryIntoHeaderValue` to support using typed +- `ResponseBuilder::content_type` now takes an `impl TryIntoHeaderValue` to support using typed `mime` types. [#1894] -* Renamed `TryIntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std +- Renamed `TryIntoHeaderValue::{try_into => try_into_value}` to avoid ambiguity with std `TryInto` trait. [#1894] -* `Extensions::insert` returns Option of replaced item. [#1904] -* Remove `HttpResponseBuilder::json2()`. [#1903] -* Enable `HttpResponseBuilder::json()` to receive data by value and reference. [#1903] -* `client::error::ConnectError` Resolver variant contains `Box` type. [#1905] -* `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905] -* Simplify `BlockingError` type to a unit struct. It's now only triggered when blocking thread pool +- `Extensions::insert` returns Option of replaced item. [#1904] +- Remove `HttpResponseBuilder::json2()`. [#1903] +- Enable `HttpResponseBuilder::json()` to receive data by value and reference. [#1903] +- `client::error::ConnectError` Resolver variant contains `Box` type. [#1905] +- `client::ConnectorConfig` default timeout changed to 5 seconds. [#1905] +- Simplify `BlockingError` type to a unit struct. It's now only triggered when blocking thread pool is dead. [#1957] -* `HeaderMap::len` now returns number of values instead of number of keys. [#1964] -* `HeaderMap::insert` now returns iterator of removed values. [#1964] -* `HeaderMap::remove` now returns iterator of removed values. [#1964] +- `HeaderMap::len` now returns number of values instead of number of keys. [#1964] +- `HeaderMap::insert` now returns iterator of removed values. [#1964] +- `HeaderMap::remove` now returns iterator of removed values. [#1964] ### Removed -* `ResponseBuilder::set`; use `ResponseBuilder::insert_header`. [#1869] -* `ResponseBuilder::set_header`; use `ResponseBuilder::insert_header`. [#1869] -* `ResponseBuilder::header`; use `ResponseBuilder::append_header`. [#1869] -* `TestRequest::with_hdr`; use `TestRequest::default().insert_header()`. [#1869] -* `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869] -* `actors` optional feature. [#1969] -* `ResponseError` impl for `actix::MailboxError`. [#1969] +- `ResponseBuilder::set`; use `ResponseBuilder::insert_header`. [#1869] +- `ResponseBuilder::set_header`; use `ResponseBuilder::insert_header`. [#1869] +- `ResponseBuilder::header`; use `ResponseBuilder::append_header`. [#1869] +- `TestRequest::with_hdr`; use `TestRequest::default().insert_header()`. [#1869] +- `TestRequest::with_header`; use `TestRequest::default().insert_header()`. [#1869] +- `actors` optional feature. [#1969] +- `ResponseError` impl for `actix::MailboxError`. [#1969] ### Documentation -* Vastly improve docs and add examples for `HeaderMap`. [#1964] +- Vastly improve docs and add examples for `HeaderMap`. [#1964] [#1869]: https://github.com/actix/actix-web/pull/1869 [#1894]: https://github.com/actix/actix-web/pull/1894 @@ -333,24 +333,24 @@ ## 3.0.0-beta.1 - 2021-01-07 ### Added -* Add `Http3` to `Protocol` enum for future compatibility and also mark `#[non_exhaustive]`. +- Add `Http3` to `Protocol` enum for future compatibility and also mark `#[non_exhaustive]`. ### Changed -* Update `actix-*` dependencies to tokio `1.0` based versions. [#1813] -* Bumped `rand` to `0.8`. -* Update `bytes` to `1.0`. [#1813] -* Update `h2` to `0.3`. [#1813] -* The `ws::Message::Text` enum variant now contains a `bytestring::ByteString`. [#1864] +- Update `actix-*` dependencies to tokio `1.0` based versions. [#1813] +- Bumped `rand` to `0.8`. +- Update `bytes` to `1.0`. [#1813] +- Update `h2` to `0.3`. [#1813] +- The `ws::Message::Text` enum variant now contains a `bytestring::ByteString`. [#1864] ### Removed -* Deprecated `on_connect` methods have been removed. Prefer the new +- Deprecated `on_connect` methods have been removed. Prefer the new `on_connect_ext` technique. [#1857] -* Remove `ResponseError` impl for `actix::actors::resolver::ResolverError` +- Remove `ResponseError` impl for `actix::actors::resolver::ResolverError` due to deprecate of resolver actor. [#1813] -* Remove `ConnectError::SslHandshakeError` and re-export of `HandshakeError`. +- Remove `ConnectError::SslHandshakeError` and re-export of `HandshakeError`. due to the removal of this type from `tokio-openssl` crate. openssl handshake error would return as `ConnectError::SslError`. [#1813] -* Remove `actix-threadpool` dependency. Use `actix_rt::task::spawn_blocking`. +- Remove `actix-threadpool` dependency. Use `actix_rt::task::spawn_blocking`. Due to this change `actix_threadpool::BlockingError` type is moved into `actix_http::error` module. [#1878] @@ -362,20 +362,20 @@ ## 2.2.1 - 2021-08-09 ### Fixed -* Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977) +- Potential HTTP request smuggling vulnerabilities. [RUSTSEC-2021-0081](https://github.com/rustsec/advisory-db/pull/977) ## 2.2.0 - 2020-11-25 ### Added -* HttpResponse builders for 1xx status codes. [#1768] -* `Accept::mime_precedence` and `Accept::mime_preference`. [#1793] -* `TryFrom` and `TryFrom` for `http::header::Quality`. [#1797] +- HttpResponse builders for 1xx status codes. [#1768] +- `Accept::mime_precedence` and `Accept::mime_preference`. [#1793] +- `TryFrom` and `TryFrom` for `http::header::Quality`. [#1797] ### Fixed -* Started dropping `transfer-encoding: chunked` and `Content-Length` for 1XX and 204 responses. [#1767] +- Started dropping `transfer-encoding: chunked` and `Content-Length` for 1XX and 204 responses. [#1767] ### Changed -* Upgrade `serde_urlencoded` to `0.7`. [#1773] +- Upgrade `serde_urlencoded` to `0.7`. [#1773] [#1773]: https://github.com/actix/actix-web/pull/1773 [#1767]: https://github.com/actix/actix-web/pull/1767 @@ -386,12 +386,12 @@ ## 2.1.0 - 2020-10-30 ### Added -* Added more flexible `on_connect_ext` methods for on-connect handling. [#1754] +- Added more flexible `on_connect_ext` methods for on-connect handling. [#1754] ### Changed -* Upgrade `base64` to `0.13`. [#1744] -* Upgrade `pin-project` to `1.0`. [#1733] -* Deprecate `ResponseBuilder::{if_some, if_true}`. [#1760] +- Upgrade `base64` to `0.13`. [#1744] +- Upgrade `pin-project` to `1.0`. [#1733] +- Deprecate `ResponseBuilder::{if_some, if_true}`. [#1760] [#1760]: https://github.com/actix/actix-web/pull/1760 [#1754]: https://github.com/actix/actix-web/pull/1754 @@ -400,28 +400,28 @@ ## 2.0.0 - 2020-09-11 -* No significant changes from `2.0.0-beta.4`. +- No significant changes from `2.0.0-beta.4`. ## 2.0.0-beta.4 - 2020-09-09 ### Changed -* Update actix-codec and actix-utils dependencies. -* Update actix-connect and actix-tls dependencies. +- Update actix-codec and actix-utils dependencies. +- Update actix-connect and actix-tls dependencies. ## 2.0.0-beta.3 - 2020-08-14 ### Fixed -* Memory leak of `client::pool::ConnectorPoolSupport`. [#1626] +- Memory leak of `client::pool::ConnectorPoolSupport`. [#1626] [#1626]: https://github.com/actix/actix-web/pull/1626 ## 2.0.0-beta.2 - 2020-07-21 ### Fixed -* Potential UB in h1 decoder using uninitialized memory. [#1614] +- Potential UB in h1 decoder using uninitialized memory. [#1614] ### Changed -* Fix illegal chunked encoding. [#1615] +- Fix illegal chunked encoding. [#1615] [#1614]: https://github.com/actix/actix-web/pull/1614 [#1615]: https://github.com/actix/actix-web/pull/1615 @@ -429,10 +429,10 @@ ## 2.0.0-beta.1 - 2020-07-11 ### Changed -* Migrate cookie handling to `cookie` crate. [#1558] -* Update `sha-1` to 0.9. [#1586] -* Fix leak in client pool. [#1580] -* MSRV is now 1.41.1. +- Migrate cookie handling to `cookie` crate. [#1558] +- Update `sha-1` to 0.9. [#1586] +- Fix leak in client pool. [#1580] +- MSRV is now 1.41.1. [#1558]: https://github.com/actix/actix-web/pull/1558 [#1586]: https://github.com/actix/actix-web/pull/1586 @@ -441,15 +441,15 @@ ## 2.0.0-alpha.4 - 2020-05-21 ### Changed -* Bump minimum supported Rust version to 1.40 -* content_length function is removed, and you can set Content-Length by calling +- Bump minimum supported Rust version to 1.40 +- content_length function is removed, and you can set Content-Length by calling no_chunking function [#1439] -* `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a +- `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a `u64` instead of a `usize`. -* Update `base64` dependency to 0.12 +- Update `base64` dependency to 0.12 ### Fixed -* Support parsing of `SameSite=None` [#1503] +- Support parsing of `SameSite=None` [#1503] [#1439]: https://github.com/actix/actix-web/pull/1439 [#1503]: https://github.com/actix/actix-web/pull/1503 @@ -457,13 +457,13 @@ ## 2.0.0-alpha.3 - 2020-05-08 ### Fixed -* Correct spelling of ConnectError::Unresolved [#1487] -* Fix a mistake in the encoding of websocket continuation messages wherein +- Correct spelling of ConnectError::Unresolved [#1487] +- Fix a mistake in the encoding of websocket continuation messages wherein Item::FirstText and Item::FirstBinary are each encoded as the other. ### Changed -* Implement `std::error::Error` for our custom errors [#1422] -* Remove `failure` support for `ResponseError` since that crate +- Implement `std::error::Error` for our custom errors [#1422] +- Remove `failure` support for `ResponseError` since that crate will be deprecated in the near future. [#1422]: https://github.com/actix/actix-web/pull/1422 @@ -472,12 +472,12 @@ ## 2.0.0-alpha.2 - 2020-03-07 ### Changed -* Update `actix-connect` and `actix-tls` dependency to 2.0.0-alpha.1. [#1395] -* Change default initial window size and connection window size for HTTP2 to 2MB and 1MB +- Update `actix-connect` and `actix-tls` dependency to 2.0.0-alpha.1. [#1395] +- Change default initial window size and connection window size for HTTP2 to 2MB and 1MB respectively to improve download speed for awc when downloading large objects. [#1394] -* client::Connector accepts initial_window_size and initial_connection_window_size +- client::Connector accepts initial_window_size and initial_connection_window_size HTTP2 configuration. [#1394] -* client::Connector allowing to set max_http_version to limit HTTP version to be used. [#1394] +- client::Connector allowing to set max_http_version to limit HTTP version to be used. [#1394] [#1394]: https://github.com/actix/actix-web/pull/1394 [#1395]: https://github.com/actix/actix-web/pull/1395 @@ -485,61 +485,61 @@ ## 2.0.0-alpha.1 - 2020-02-27 ### Changed -* Update the `time` dependency to 0.2.7. -* Moved actors messages support from actix crate, enabled with feature `actors`. -* Breaking change: trait MessageBody requires Unpin and accepting `Pin<&mut Self>` instead of +- Update the `time` dependency to 0.2.7. +- Moved actors messages support from actix crate, enabled with feature `actors`. +- Breaking change: trait MessageBody requires Unpin and accepting `Pin<&mut Self>` instead of `&mut self` in the poll_next(). -* MessageBody is not implemented for &'static [u8] anymore. +- MessageBody is not implemented for &'static [u8] anymore. ### Fixed -* Allow `SameSite=None` cookies to be sent in a response. +- Allow `SameSite=None` cookies to be sent in a response. ## 1.0.1 - 2019-12-20 ### Fixed -* Poll upgrade service's readiness from HTTP service handlers -* Replace brotli with brotli2 #1224 +- Poll upgrade service's readiness from HTTP service handlers +- Replace brotli with brotli2 #1224 ## 1.0.0 - 2019-12-13 ### Added -* Add websockets continuation frame support +- Add websockets continuation frame support ### Changed -* Replace `flate2-xxx` features with `compress` +- Replace `flate2-xxx` features with `compress` ## 1.0.0-alpha.5 - 2019-12-09 ### Fixed -* Check `Upgrade` service readiness before calling it -* Fix buffer remaining capacity calculation +- Check `Upgrade` service readiness before calling it +- Fix buffer remaining capacity calculation ### Changed -* Websockets: Ping and Pong should have binary data #1049 +- Websockets: Ping and Pong should have binary data #1049 ## 1.0.0-alpha.4 - 2019-12-08 ### Added -* Add impl ResponseBuilder for Error +- Add impl ResponseBuilder for Error ### Changed -* Use rust based brotli compression library +- Use rust based brotli compression library ## 1.0.0-alpha.3 - 2019-12-07 ### Changed -* Migrate to tokio 0.2 -* Migrate to `std::future` +- Migrate to tokio 0.2 +- Migrate to `std::future` ## 0.2.11 - 2019-11-06 ### Added -* Add support for serde_json::Value to be passed as argument to ResponseBuilder.body() -* Add an additional `filename*` param in the `Content-Disposition` header of +- Add support for serde_json::Value to be passed as argument to ResponseBuilder.body() +- Add an additional `filename*` param in the `Content-Disposition` header of `actix_files::NamedFile` to be more compatible. (#1151) -* Allow to use `std::convert::Infallible` as `actix_http::error::Error` +- Allow to use `std::convert::Infallible` as `actix_http::error::Error` ### Fixed -* To be compatible with non-English error responses, `ResponseError` rendered with `text/plain; +- To be compatible with non-English error responses, `ResponseError` rendered with `text/plain; charset=utf-8` header [#1118] [#1878]: https://github.com/actix/actix-web/pull/1878 @@ -547,169 +547,169 @@ ## 0.2.10 - 2019-09-11 ### Added -* Add support for sending HTTP requests with `Rc` in addition to sending HTTP requests +- Add support for sending HTTP requests with `Rc` in addition to sending HTTP requests with `RequestHead` ### Fixed -* h2 will use error response #1080 -* on_connect result isn't added to request extensions for http2 requests #1009 +- h2 will use error response #1080 +- on_connect result isn't added to request extensions for http2 requests #1009 ## 0.2.9 - 2019-08-13 ### Changed -* Dropped the `byteorder`-dependency in favor of `stdlib`-implementation -* Update percent-encoding to 2.1 -* Update serde_urlencoded to 0.6.1 +- Dropped the `byteorder`-dependency in favor of `stdlib`-implementation +- Update percent-encoding to 2.1 +- Update serde_urlencoded to 0.6.1 ### Fixed -* Fixed a panic in the HTTP2 handshake in client HTTP requests (#1031) +- Fixed a panic in the HTTP2 handshake in client HTTP requests (#1031) ## 0.2.8 - 2019-08-01 ### Added -* Add `rustls` support -* Add `Clone` impl for `HeaderMap` +- Add `rustls` support +- Add `Clone` impl for `HeaderMap` ### Fixed -* awc client panic #1016 -* Invalid response with compression middleware enabled, but compression-related features +- awc client panic #1016 +- Invalid response with compression middleware enabled, but compression-related features disabled #997 ## 0.2.7 - 2019-07-18 ### Added -* Add support for downcasting response errors #986 +- Add support for downcasting response errors #986 ## 0.2.6 - 2019-07-17 ### Changed -* Replace `ClonableService` with local copy -* Upgrade `rand` dependency version to 0.7 +- Replace `ClonableService` with local copy +- Upgrade `rand` dependency version to 0.7 ## 0.2.5 - 2019-06-28 ### Added -* Add `on-connect` callback, `HttpServiceBuilder::on_connect()` #946 +- Add `on-connect` callback, `HttpServiceBuilder::on_connect()` #946 ### Changed -* Use `encoding_rs` crate instead of unmaintained `encoding` crate -* Add `Copy` and `Clone` impls for `ws::Codec` +- Use `encoding_rs` crate instead of unmaintained `encoding` crate +- Add `Copy` and `Clone` impls for `ws::Codec` ## 0.2.4 - 2019-06-16 ### Fixed -* Do not compress NoContent (204) responses #918 +- Do not compress NoContent (204) responses #918 ## 0.2.3 - 2019-06-02 ### Added -* Debug impl for ResponseBuilder -* From SizedStream and BodyStream for Body +- Debug impl for ResponseBuilder +- From SizedStream and BodyStream for Body ### Changed -* SizedStream uses u64 +- SizedStream uses u64 ## 0.2.2 - 2019-05-29 ### Fixed -* Parse incoming stream before closing stream on disconnect #868 +- Parse incoming stream before closing stream on disconnect #868 ## 0.2.1 - 2019-05-25 ### Fixed -* Handle socket read disconnect +- Handle socket read disconnect ## 0.2.0 - 2019-05-12 ### Changed -* Update actix-service to 0.4 -* Expect and upgrade services accept `ServerConfig` config. +- Update actix-service to 0.4 +- Expect and upgrade services accept `ServerConfig` config. ### Deleted -* `OneRequest` service +- `OneRequest` service ## 0.1.5 - 2019-05-04 ### Fixed -* Clean up response extensions in response pool #817 +- Clean up response extensions in response pool #817 ## 0.1.4 - 2019-04-24 ### Added -* Allow to render h1 request headers in `Camel-Case` +- Allow to render h1 request headers in `Camel-Case` ### Fixed -* Read until eof for http/1.0 responses #771 +- Read until eof for http/1.0 responses #771 ## 0.1.3 - 2019-04-23 ### Fixed -* Fix http client pool management -* Fix http client wait queue management #794 +- Fix http client pool management +- Fix http client wait queue management #794 ## 0.1.2 - 2019-04-23 ### Fixed -* Fix BorrowMutError panic in client connector #793 +- Fix BorrowMutError panic in client connector #793 ## 0.1.1 - 2019-04-19 ### Changed -* Cookie::max_age() accepts value in seconds -* Cookie::max_age_time() accepts value in time::Duration -* Allow to specify server address for client connector +- Cookie::max_age() accepts value in seconds +- Cookie::max_age_time() accepts value in time::Duration +- Allow to specify server address for client connector ## 0.1.0 - 2019-04-16 ### Added -* Expose peer addr via `Request::peer_addr()` and `RequestHead::peer_addr` +- Expose peer addr via `Request::peer_addr()` and `RequestHead::peer_addr` ### Changed -* `actix_http::encoding` always available -* use trust-dns-resolver 0.11.0 +- `actix_http::encoding` always available +- use trust-dns-resolver 0.11.0 ## 0.1.0-alpha.5 - 2019-04-12 ### Added -* Allow to use custom service for upgrade requests -* Added `h1::SendResponse` future. +- Allow to use custom service for upgrade requests +- Added `h1::SendResponse` future. ### Changed -* MessageBody::length() renamed to MessageBody::size() for consistency -* ws handshake verification functions take RequestHead instead of Request +- MessageBody::length() renamed to MessageBody::size() for consistency +- ws handshake verification functions take RequestHead instead of Request ## 0.1.0-alpha.4 - 2019-04-08 ### Added -* Allow to use custom `Expect` handler -* Add minimal `std::error::Error` impl for `Error` +- Allow to use custom `Expect` handler +- Add minimal `std::error::Error` impl for `Error` ### Changed -* Export IntoHeaderValue -* Render error and return as response body -* Use thread pool for response body compression +- Export IntoHeaderValue +- Render error and return as response body +- Use thread pool for response body compression ### Deleted -* Removed PayloadBuffer +- Removed PayloadBuffer ## 0.1.0-alpha.3 - 2019-04-02 ### Added -* Warn when an unsealed private cookie isn't valid UTF-8 +- Warn when an unsealed private cookie isn't valid UTF-8 ### Fixed -* Rust 1.31.0 compatibility -* Preallocate read buffer for h1 codec -* Detect socket disconnection during protocol selection +- Rust 1.31.0 compatibility +- Preallocate read buffer for h1 codec +- Detect socket disconnection during protocol selection ## 0.1.0-alpha.2 - 2019-03-29 ### Added -* Added ws::Message::Nop, no-op websockets message +- Added ws::Message::Nop, no-op websockets message ### Changed -* Do not use thread pool for decompression if chunk size is smaller than 2048. +- Do not use thread pool for decompression if chunk size is smaller than 2048. ## 0.1.0-alpha.1 - 2019-03-28 -* Initial impl +- Initial impl diff --git a/actix-http/README.md b/actix-http/README.md index 731d7a48e..05edffd2c 100644 --- a/actix-http/README.md +++ b/actix-http/README.md @@ -54,8 +54,8 @@ async fn main() -> io::Result<()> { This project is licensed under either of -* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)) -* MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)) +- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)) at your option. diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index 8d9c1640f..e58c3ee24 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -4,119 +4,119 @@ ## 0.4.0-beta.10 - 2021-12-11 -* No significant changes since `0.4.0-beta.9`. +- No significant changes since `0.4.0-beta.9`. ## 0.4.0-beta.9 - 2021-12-01 -* Polling `Field` after dropping `Multipart` now fails immediately instead of hanging forever. [#2463] +- Polling `Field` after dropping `Multipart` now fails immediately instead of hanging forever. [#2463] [#2463]: https://github.com/actix/actix-web/pull/2463 ## 0.4.0-beta.8 - 2021-11-22 -* Ensure a correct Content-Disposition header is included in every part of a multipart message. [#2451] -* Added `MultipartError::NoContentDisposition` variant. [#2451] -* Since Content-Disposition is now ensured, `Field::content_disposition` is now infallible. [#2451] -* Added `Field::name` method for getting the field name. [#2451] -* `MultipartError` now marks variants with inner errors as the source. [#2451] -* `MultipartError` is now marked as non-exhaustive. [#2451] +- Ensure a correct Content-Disposition header is included in every part of a multipart message. [#2451] +- Added `MultipartError::NoContentDisposition` variant. [#2451] +- Since Content-Disposition is now ensured, `Field::content_disposition` is now infallible. [#2451] +- Added `Field::name` method for getting the field name. [#2451] +- `MultipartError` now marks variants with inner errors as the source. [#2451] +- `MultipartError` is now marked as non-exhaustive. [#2451] [#2451]: https://github.com/actix/actix-web/pull/2451 ## 0.4.0-beta.7 - 2021-10-20 -* Minimum supported Rust version (MSRV) is now 1.52. +- Minimum supported Rust version (MSRV) is now 1.52. ## 0.4.0-beta.6 - 2021-09-09 -* Minimum supported Rust version (MSRV) is now 1.51. +- Minimum supported Rust version (MSRV) is now 1.51. ## 0.4.0-beta.5 - 2021-06-17 -* No notable changes. +- No notable changes. ## 0.4.0-beta.4 - 2021-04-02 -* No notable changes. +- No notable changes. ## 0.4.0-beta.3 - 2021-03-09 -* No notable changes. +- No notable changes. ## 0.4.0-beta.2 - 2021-02-10 -* No notable changes. +- No notable changes. ## 0.4.0-beta.1 - 2021-01-07 -* Fix multipart consuming payload before header checks. [#1513] -* Update `bytes` to `1.0`. [#1813] +- Fix multipart consuming payload before header checks. [#1513] +- Update `bytes` to `1.0`. [#1813] [#1813]: https://github.com/actix/actix-web/pull/1813 [#1513]: https://github.com/actix/actix-web/pull/1513 ## 0.3.0 - 2020-09-11 -* No significant changes from `0.3.0-beta.2`. +- No significant changes from `0.3.0-beta.2`. ## 0.3.0-beta.2 - 2020-09-10 -* Update `actix-*` dependencies to latest versions. +- Update `actix-*` dependencies to latest versions. ## 0.3.0-beta.1 - 2020-07-15 -* Update `actix-web` to 3.0.0-beta.1 +- Update `actix-web` to 3.0.0-beta.1 ## 0.3.0-alpha.1 - 2020-05-25 -* Update `actix-web` to 3.0.0-alpha.3 -* Bump minimum supported Rust version to 1.40 -* Minimize `futures` dependencies -* Remove the unused `time` dependency -* Fix missing `std::error::Error` implement for `MultipartError`. +- Update `actix-web` to 3.0.0-alpha.3 +- Bump minimum supported Rust version to 1.40 +- Minimize `futures` dependencies +- Remove the unused `time` dependency +- Fix missing `std::error::Error` implement for `MultipartError`. ## [0.2.0] - 2019-12-20 -* Release +- Release ## [0.2.0-alpha.4] - 2019-12-xx -* Multipart handling now handles Pending during read of boundary #1205 +- Multipart handling now handles Pending during read of boundary #1205 ## [0.2.0-alpha.2] - 2019-12-03 -* Migrate to `std::future` +- Migrate to `std::future` ## [0.1.4] - 2019-09-12 -* Multipart handling now parses requests which do not end in CRLF #1038 +- Multipart handling now parses requests which do not end in CRLF #1038 ## [0.1.3] - 2019-08-18 -* Fix ring dependency from actix-web default features for #741. +- Fix ring dependency from actix-web default features for #741. ## [0.1.2] - 2019-06-02 -* Fix boundary parsing #876 +- Fix boundary parsing #876 ## [0.1.1] - 2019-05-25 -* Fix disconnect handling #834 +- Fix disconnect handling #834 ## [0.1.0] - 2019-05-18 -* Release +- Release ## [0.1.0-beta.4] - 2019-05-12 -* Handle cancellation of uploads #736 +- Handle cancellation of uploads #736 -* Upgrade to actix-web 1.0.0-beta.4 +- Upgrade to actix-web 1.0.0-beta.4 ## [0.1.0-beta.1] - 2019-04-21 -* Do not support nested multipart +- Do not support nested multipart -* Split multipart support to separate crate +- Split multipart support to separate crate -* Optimize multipart handling #634, #769 +- Optimize multipart handling #634, #769 diff --git a/actix-router/CHANGES.md b/actix-router/CHANGES.md index d0ed55c88..0a6a56359 100644 --- a/actix-router/CHANGES.md +++ b/actix-router/CHANGES.md @@ -4,20 +4,20 @@ ## 0.5.0-beta.3 - 2021-12-17 -* Minimum supported Rust version (MSRV) is now 1.52. +- Minimum supported Rust version (MSRV) is now 1.52. ## 0.5.0-beta.2 - 2021-09-09 -* Introduce `ResourceDef::join`. [#380] -* Disallow prefix routes with tail segments. [#379] -* Enforce path separators on dynamic prefixes. [#378] -* Improve malformed path error message. [#384] -* Prefix segments now always end with with a segment delimiter or end-of-input. [#2355] -* Prefix segments with trailing slashes define a trailing empty segment. [#2355] -* Support multi-pattern prefixes and joins. [#2356] -* `ResourceDef::pattern` now returns the first pattern in multi-pattern resources. [#2356] -* Support `build_resource_path` on multi-pattern resources. [#2356] -* Minimum supported Rust version (MSRV) is now 1.51. +- Introduce `ResourceDef::join`. [#380] +- Disallow prefix routes with tail segments. [#379] +- Enforce path separators on dynamic prefixes. [#378] +- Improve malformed path error message. [#384] +- Prefix segments now always end with with a segment delimiter or end-of-input. [#2355] +- Prefix segments with trailing slashes define a trailing empty segment. [#2355] +- Support multi-pattern prefixes and joins. [#2356] +- `ResourceDef::pattern` now returns the first pattern in multi-pattern resources. [#2356] +- Support `build_resource_path` on multi-pattern resources. [#2356] +- Minimum supported Rust version (MSRV) is now 1.51. [#378]: https://github.com/actix/actix-net/pull/378 [#379]: https://github.com/actix/actix-net/pull/379 @@ -28,23 +28,23 @@ ## 0.5.0-beta.1 - 2021-07-20 -* Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366] -* Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373] -* Fix segment interpolation leaving `Path` in unintended state after matching. [#368] -* Fix `ResourceDef` `PartialEq` implementation. [#373] -* Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372] -* Implement `IntoPatterns` for `bytestring::ByteString`. [#372] -* Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370] -* Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371] -* `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373] -* Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371] -* Rename `ResourceDef::{is_prefix_match => find_match}`. [#373] -* Rename `ResourceDef::{match_path => capture_match_info}`. [#373] -* Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373] -* Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373] -* Rename `Router::{*_checked => *_fn}`. [#373] -* Return type of `ResourceDef::name` is now `Option<&str>`. [#373] -* Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373] +- Fix a bug in multi-patterns where static patterns are interpreted as regex. [#366] +- Introduce `ResourceDef::pattern_iter` to get an iterator over all patterns in a multi-pattern resource. [#373] +- Fix segment interpolation leaving `Path` in unintended state after matching. [#368] +- Fix `ResourceDef` `PartialEq` implementation. [#373] +- Re-work `IntoPatterns` trait, adding a `Patterns` enum. [#372] +- Implement `IntoPatterns` for `bytestring::ByteString`. [#372] +- Rename `Path::{len => segment_count}` to be more descriptive of it's purpose. [#370] +- Rename `ResourceDef::{resource_path => resource_path_from_iter}`. [#371] +- `ResourceDef::resource_path_from_iter` now takes an `IntoIterator`. [#373] +- Rename `ResourceDef::{resource_path_named => resource_path_from_map}`. [#371] +- Rename `ResourceDef::{is_prefix_match => find_match}`. [#373] +- Rename `ResourceDef::{match_path => capture_match_info}`. [#373] +- Rename `ResourceDef::{match_path_checked => capture_match_info_fn}`. [#373] +- Remove `ResourceDef::name_mut` and introduce `ResourceDef::set_name`. [#373] +- Rename `Router::{*_checked => *_fn}`. [#373] +- Return type of `ResourceDef::name` is now `Option<&str>`. [#373] +- Return type of `ResourceDef::pattern` is now `Option<&str>`. [#373] [#368]: https://github.com/actix/actix-net/pull/368 [#366]: https://github.com/actix/actix-net/pull/366 @@ -56,10 +56,10 @@ ## 0.4.0 - 2021-06-06 -* When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357] -* Path tail patterns now match new lines (`\n`) in request URL. [#360] -* Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359] -* Methods `Path::{add, add_static}` now take `impl Into>`. [#345] +- When matching path parameters, `%25` is now kept in the percent-encoded form; no longer decoded to `%`. [#357] +- Path tail patterns now match new lines (`\n`) in request URL. [#360] +- Fixed a safety bug where `Path` could return a malformed string after percent decoding. [#359] +- Methods `Path::{add, add_static}` now take `impl Into>`. [#345] [#345]: https://github.com/actix/actix-net/pull/345 [#357]: https://github.com/actix/actix-net/pull/357 @@ -68,68 +68,68 @@ ## 0.3.0 - 2019-12-31 -* Version was yanked previously. See https://crates.io/crates/actix-router/0.3.0 +- Version was yanked previously. See https://crates.io/crates/actix-router/0.3.0 ## 0.2.7 - 2021-02-06 -* Add `Router::recognize_checked` [#247] +- Add `Router::recognize_checked` [#247] [#247]: https://github.com/actix/actix-net/pull/247 ## 0.2.6 - 2021-01-09 -* Use `bytestring` version range compatible with Bytes v1.0. [#246] +- Use `bytestring` version range compatible with Bytes v1.0. [#246] [#246]: https://github.com/actix/actix-net/pull/246 ## 0.2.5 - 2020-09-20 -* Fix `from_hex()` method +- Fix `from_hex()` method ## 0.2.4 - 2019-12-31 -* Add `ResourceDef::resource_path_named()` path generation method +- Add `ResourceDef::resource_path_named()` path generation method ## 0.2.3 - 2019-12-25 -* Add impl `IntoPattern` for `&String` +- Add impl `IntoPattern` for `&String` ## 0.2.2 - 2019-12-25 -* Use `IntoPattern` for `RouterBuilder::path()` +- Use `IntoPattern` for `RouterBuilder::path()` ## 0.2.1 - 2019-12-25 -* Add `IntoPattern` trait -* Add multi-pattern resources +- Add `IntoPattern` trait +- Add multi-pattern resources ## 0.2.0 - 2019-12-07 -* Update http to 0.2 -* Update regex to 1.3 -* Use bytestring instead of string +- Update http to 0.2 +- Update regex to 1.3 +- Use bytestring instead of string ## 0.1.5 - 2019-05-15 -* Remove debug prints +- Remove debug prints ## 0.1.4 - 2019-05-15 -* Fix checked resource match +- Fix checked resource match ## 0.1.3 - 2019-04-22 -* Added support for `remainder match` (i.e "/path/{tail}*") +- Added support for `remainder match` (i.e "/path/{tail}*") ## 0.1.2 - 2019-04-07 -* Export `Quoter` type -* Allow to reset `Path` instance +- Export `Quoter` type +- Allow to reset `Path` instance ## 0.1.1 - 2019-04-03 -* Get dynamic segment by name instead of iterator. +- Get dynamic segment by name instead of iterator. ## 0.1.0 - 2019-03-09 -* Initial release +- Initial release diff --git a/actix-test/CHANGES.md b/actix-test/CHANGES.md index ef78ac54a..e3deeb3f4 100644 --- a/actix-test/CHANGES.md +++ b/actix-test/CHANGES.md @@ -4,46 +4,46 @@ ## 0.1.0-beta.9 - 2021-12-17 -* Re-export `actix_http::body::to_bytes`. [#2518] -* Update `actix_web::test` re-exports. [#2518] +- Re-export `actix_http::body::to_bytes`. [#2518] +- Update `actix_web::test` re-exports. [#2518] [#2518]: https://github.com/actix/actix-web/pull/2518 ## 0.1.0-beta.8 - 2021-12-11 -* No significant changes since `0.1.0-beta.7`. +- No significant changes since `0.1.0-beta.7`. ## 0.1.0-beta.7 - 2021-11-22 -* Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408] +- Fix compatibility with experimental `io-uring` feature of `actix-rt`. [#2408] [#2408]: https://github.com/actix/actix-web/pull/2408 ## 0.1.0-beta.6 - 2021-11-15 -* No significant changes from `0.1.0-beta.5`. +- No significant changes from `0.1.0-beta.5`. ## 0.1.0-beta.5 - 2021-10-20 -* Updated rustls to v0.20. [#2414] -* Minimum supported Rust version (MSRV) is now 1.52. +- Updated rustls to v0.20. [#2414] +- Minimum supported Rust version (MSRV) is now 1.52. [#2414]: https://github.com/actix/actix-web/pull/2414 ## 0.1.0-beta.4 - 2021-09-09 -* Minimum supported Rust version (MSRV) is now 1.51. +- Minimum supported Rust version (MSRV) is now 1.51. ## 0.1.0-beta.3 - 2021-06-20 -* No significant changes from `0.1.0-beta.2`. +- No significant changes from `0.1.0-beta.2`. ## 0.1.0-beta.2 - 2021-04-17 -* No significant changes from `0.1.0-beta.1`. +- No significant changes from `0.1.0-beta.1`. ## 0.1.0-beta.1 - 2021-04-02 -* Move integration testing structs from `actix-web`. [#2112] +- Move integration testing structs from `actix-web`. [#2112] [#2112]: https://github.com/actix/actix-web/pull/2112 diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md index d3078499c..6abfe2c61 100644 --- a/actix-web-actors/CHANGES.md +++ b/actix-web-actors/CHANGES.md @@ -4,105 +4,105 @@ ## 4.0.0-beta.8 - 2021-12-11 -* Add `ws:WsResponseBuilder` for building WebSocket session response. [#1920] -* Deprecate `ws::{start_with_addr, start_with_protocols}`. [#1920] -* Minimum supported Rust version (MSRV) is now 1.52. +- Add `ws:WsResponseBuilder` for building WebSocket session response. [#1920] +- Deprecate `ws::{start_with_addr, start_with_protocols}`. [#1920] +- Minimum supported Rust version (MSRV) is now 1.52. [#1920]: https://github.com/actix/actix-web/pull/1920 ## 4.0.0-beta.7 - 2021-09-09 -* Minimum supported Rust version (MSRV) is now 1.51. +- Minimum supported Rust version (MSRV) is now 1.51. ## 4.0.0-beta.6 - 2021-06-26 -* Update `actix` to `0.12`. [#2277] +- Update `actix` to `0.12`. [#2277] [#2277]: https://github.com/actix/actix-web/pull/2277 ## 4.0.0-beta.5 - 2021-06-17 -* No notable changes. +- No notable changes. ## 4.0.0-beta.4 - 2021-04-02 -* No notable changes. +- No notable changes. ## 4.0.0-beta.3 - 2021-03-09 -* No notable changes. +- No notable changes. ## 4.0.0-beta.2 - 2021-02-10 -* No notable changes. +- No notable changes. ## 4.0.0-beta.1 - 2021-01-07 -* Update `pin-project` to `1.0`. -* Update `bytes` to `1.0`. [#1813] -* `WebsocketContext::text` now takes an `Into`. [#1864] +- Update `pin-project` to `1.0`. +- Update `bytes` to `1.0`. [#1813] +- `WebsocketContext::text` now takes an `Into`. [#1864] [#1813]: https://github.com/actix/actix-web/pull/1813 [#1864]: https://github.com/actix/actix-web/pull/1864 ## 3.0.0 - 2020-09-11 -* No significant changes from `3.0.0-beta.2`. +- No significant changes from `3.0.0-beta.2`. ## 3.0.0-beta.2 - 2020-09-10 -* Update `actix-*` dependencies to latest versions. +- Update `actix-*` dependencies to latest versions. ## [3.0.0-beta.1] - 2020-xx-xx -* Update `actix-web` & `actix-http` dependencies to beta.1 -* Bump minimum supported Rust version to 1.40 +- Update `actix-web` & `actix-http` dependencies to beta.1 +- Bump minimum supported Rust version to 1.40 ## [3.0.0-alpha.1] - 2020-05-08 -* Update the actix-web dependency to 3.0.0-alpha.1 -* Update the actix dependency to 0.10.0-alpha.2 -* Update the actix-http dependency to 2.0.0-alpha.3 +- Update the actix-web dependency to 3.0.0-alpha.1 +- Update the actix dependency to 0.10.0-alpha.2 +- Update the actix-http dependency to 2.0.0-alpha.3 ## [2.0.0] - 2019-12-20 -* Release +- Release ## [2.0.0-alpha.1] - 2019-12-15 -* Migrate to actix-web 2.0.0 +- Migrate to actix-web 2.0.0 ## [1.0.4] - 2019-12-07 -* Allow comma-separated websocket subprotocols without spaces (#1172) +- Allow comma-separated websocket subprotocols without spaces (#1172) ## [1.0.3] - 2019-11-14 -* Update actix-web and actix-http dependencies +- Update actix-web and actix-http dependencies ## [1.0.2] - 2019-07-20 -* Add `ws::start_with_addr()`, returning the address of the created actor, along +- Add `ws::start_with_addr()`, returning the address of the created actor, along with the `HttpResponse`. -* Add support for specifying protocols on websocket handshake #835 +- Add support for specifying protocols on websocket handshake #835 ## [1.0.1] - 2019-06-28 -* Allow to use custom ws codec with `WebsocketContext` #925 +- Allow to use custom ws codec with `WebsocketContext` #925 ## [1.0.0] - 2019-05-29 -* Update actix-http and actix-web +- Update actix-http and actix-web ## [0.1.0-alpha.3] - 2019-04-02 -* Update actix-http and actix-web +- Update actix-http and actix-web ## [0.1.0-alpha.2] - 2019-03-29 -* Update actix-http and actix-web +- Update actix-http and actix-web ## [0.1.0-alpha.1] - 2019-03-28 -* Initial impl +- Initial impl diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index 309274563..0d881d303 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -4,101 +4,101 @@ ## 0.5.0-beta.6 - 2021-12-11 -* No significant changes since `0.5.0-beta.5`. +- No significant changes since `0.5.0-beta.5`. ## 0.5.0-beta.5 - 2021-10-20 -* Improve error recovery potential when macro input is invalid. [#2410] -* Add `#[actix_web::test]` macro for setting up tests with a runtime. [#2409] -* Minimum supported Rust version (MSRV) is now 1.52. +- Improve error recovery potential when macro input is invalid. [#2410] +- Add `#[actix_web::test]` macro for setting up tests with a runtime. [#2409] +- Minimum supported Rust version (MSRV) is now 1.52. [#2410]: https://github.com/actix/actix-web/pull/2410 [#2409]: https://github.com/actix/actix-web/pull/2409 ## 0.5.0-beta.4 - 2021-09-09 -* In routing macros, paths are now validated at compile time. [#2350] -* Minimum supported Rust version (MSRV) is now 1.51. +- In routing macros, paths are now validated at compile time. [#2350] +- Minimum supported Rust version (MSRV) is now 1.51. [#2350]: https://github.com/actix/actix-web/pull/2350 ## 0.5.0-beta.3 - 2021-06-17 -* No notable changes. +- No notable changes. ## 0.5.0-beta.2 - 2021-03-09 -* Preserve doc comments when using route macros. [#2022] -* Add `name` attribute to `route` macro. [#1934] +- Preserve doc comments when using route macros. [#2022] +- Add `name` attribute to `route` macro. [#1934] [#2022]: https://github.com/actix/actix-web/pull/2022 [#1934]: https://github.com/actix/actix-web/pull/1934 ## 0.5.0-beta.1 - 2021-02-10 -* Use new call signature for `System::new`. +- Use new call signature for `System::new`. ## 0.4.0 - 2020-09-20 -* Added compile success and failure testing. [#1677] -* Add `route` macro for supporting multiple HTTP methods guards. [#1674] +- Added compile success and failure testing. [#1677] +- Add `route` macro for supporting multiple HTTP methods guards. [#1674] [#1677]: https://github.com/actix/actix-web/pull/1677 [#1674]: https://github.com/actix/actix-web/pull/1674 ## 0.3.0 - 2020-09-11 -* No significant changes from `0.3.0-beta.1`. +- No significant changes from `0.3.0-beta.1`. ## 0.3.0-beta.1 - 2020-07-14 -* Add main entry-point macro that uses re-exported runtime. [#1559] +- Add main entry-point macro that uses re-exported runtime. [#1559] [#1559]: https://github.com/actix/actix-web/pull/1559 ## 0.2.2 - 2020-05-23 -* Add resource middleware on actix-web-codegen [#1467] +- Add resource middleware on actix-web-codegen [#1467] [#1467]: https://github.com/actix/actix-web/pull/1467 ## 0.2.1 - 2020-02-25 -* Add `#[allow(missing_docs)]` attribute to generated structs [#1368] -* Allow the handler function to be named as `config` [#1290] +- Add `#[allow(missing_docs)]` attribute to generated structs [#1368] +- Allow the handler function to be named as `config` [#1290] [#1368]: https://github.com/actix/actix-web/issues/1368 [#1290]: https://github.com/actix/actix-web/issues/1290 ## 0.2.0 - 2019-12-13 -* Generate code for actix-web 2.0 +- Generate code for actix-web 2.0 ## 0.1.3 - 2019-10-14 -* Bump up `syn` & `quote` to 1.0 -* Provide better error message +- Bump up `syn` & `quote` to 1.0 +- Provide better error message ## 0.1.2 - 2019-06-04 -* Add macros for head, options, trace, connect and patch http methods +- Add macros for head, options, trace, connect and patch http methods ## 0.1.1 - 2019-06-01 -* Add syn "extra-traits" feature +- Add syn "extra-traits" feature ## 0.1.0 - 2019-05-18 -* Release +- Release ## 0.1.0-beta.1 - 2019-04-20 -* Gen code for actix-web 1.0.0-beta.1 +- Gen code for actix-web 1.0.0-beta.1 ## 0.1.0-alpha.6 - 2019-04-14 -* Gen code for actix-web 1.0.0-alpha.6 +- Gen code for actix-web 1.0.0-alpha.6 ## 0.1.0-alpha.1 - 2019-03-28 -* Initial impl +- Initial impl diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 7b822930c..b5144b7a2 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,75 +1,75 @@ # Changes ## Unreleased - 2021-xx-xx -* Rename `Connector::{ssl => openssl}`. [#2503] -* Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503] +- Rename `Connector::{ssl => openssl}`. [#2503] +- Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503] [#2503]: https://github.com/actix/actix-web/pull/2503 ## 3.0.0-beta.14 - 2021-12-17 -* Add `ClientBuilder::add_default_header` and deprecate `ClientBuilder::header`. [#2510] +- Add `ClientBuilder::add_default_header` and deprecate `ClientBuilder::header`. [#2510] [#2510]: https://github.com/actix/actix-web/pull/2510 ## 3.0.0-beta.13 - 2021-12-11 -* No significant changes since `3.0.0-beta.12`. +- No significant changes since `3.0.0-beta.12`. ## 3.0.0-beta.12 - 2021-11-30 -* Update `actix-tls` to `3.0.0-rc.1`. [#2474] +- Update `actix-tls` to `3.0.0-rc.1`. [#2474] [#2474]: https://github.com/actix/actix-web/pull/2474 ## 3.0.0-beta.11 - 2021-11-22 -* No significant changes from `3.0.0-beta.10`. +- No significant changes from `3.0.0-beta.10`. ## 3.0.0-beta.10 - 2021-11-15 -* No significant changes from `3.0.0-beta.9`. +- No significant changes from `3.0.0-beta.9`. ## 3.0.0-beta.9 - 2021-10-20 -* Updated rustls to v0.20. [#2414] +- Updated rustls to v0.20. [#2414] [#2414]: https://github.com/actix/actix-web/pull/2414 ## 3.0.0-beta.8 - 2021-09-09 ### Changed -* Send headers within the redirect requests. [#2310] +- Send headers within the redirect requests. [#2310] [#2310]: https://github.com/actix/actix-web/pull/2310 ## 3.0.0-beta.7 - 2021-06-26 ### Changed -* Change compression algorithm features flags. [#2250] +- Change compression algorithm features flags. [#2250] [#2250]: https://github.com/actix/actix-web/pull/2250 ## 3.0.0-beta.6 - 2021-06-17 -* No significant changes since 3.0.0-beta.5. +- No significant changes since 3.0.0-beta.5. ## 3.0.0-beta.5 - 2021-04-17 ### Removed -* Deprecated methods on `ClientRequest`: `if_true`, `if_some`. [#2148] +- Deprecated methods on `ClientRequest`: `if_true`, `if_some`. [#2148] [#2148]: https://github.com/actix/actix-web/pull/2148 ## 3.0.0-beta.4 - 2021-04-02 ### Added -* Add `Client::headers` to get default mut reference of `HeaderMap` of client object. [#2114] +- Add `Client::headers` to get default mut reference of `HeaderMap` of client object. [#2114] ### Changed -* `ConnectorService` type is renamed to `BoxConnectorService`. [#2081] -* Fix http/https encoding when enabling `compress` feature. [#2116] -* Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header +- `ConnectorService` type is renamed to `BoxConnectorService`. [#2081] +- Fix http/https encoding when enabling `compress` feature. [#2116] +- Rename `TestResponse::header` to `append_header`, `set` to `insert_header`. `TestResponse` header methods now take `TryIntoHeaderPair` tuples. [#2094] [#2081]: https://github.com/actix/actix-web/pull/2081 @@ -80,16 +80,16 @@ ## 3.0.0-beta.3 - 2021-03-08 ### Added -* `ClientResponse::timeout` for set the timeout of collecting response body. [#1931] -* `ClientBuilder::local_address` for bind to a local ip address for this client. [#2024] +- `ClientResponse::timeout` for set the timeout of collecting response body. [#1931] +- `ClientBuilder::local_address` for bind to a local ip address for this client. [#2024] ### Changed -* Feature `cookies` is now optional and enabled by default. [#1981] -* `ClientBuilder::connector` method would take `actix_http::client::Connector` type. [#2008] -* Basic auth password now takes blank passwords as an empty string instead of Option. [#2050] +- Feature `cookies` is now optional and enabled by default. [#1981] +- `ClientBuilder::connector` method would take `actix_http::client::Connector` type. [#2008] +- Basic auth password now takes blank passwords as an empty string instead of Option. [#2050] ### Removed -* `ClientBuilder::default` function [#2008] +- `ClientBuilder::default` function [#2008] [#1931]: https://github.com/actix/actix-web/pull/1931 [#1981]: https://github.com/actix/actix-web/pull/1981 @@ -100,18 +100,18 @@ ## 3.0.0-beta.2 - 2021-02-10 ### Added -* `ClientRequest::insert_header` method which allows using typed headers. [#1869] -* `ClientRequest::append_header` method which allows using typed headers. [#1869] -* `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969] +- `ClientRequest::insert_header` method which allows using typed headers. [#1869] +- `ClientRequest::append_header` method which allows using typed headers. [#1869] +- `trust-dns` optional feature to enable `trust-dns-resolver` as client dns resolver. [#1969] ### Changed -* Relax default timeout for `Connector` to 5 seconds(original 1 second). [#1905] +- Relax default timeout for `Connector` to 5 seconds(original 1 second). [#1905] ### Removed -* `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869] -* `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869] -* `ClientRequest::set_header_if_none`; use `ClientRequest::insert_header_if_none`. [#1869] -* `ClientRequest::header`; use `ClientRequest::append_header`. [#1869] +- `ClientRequest::set`; use `ClientRequest::insert_header`. [#1869] +- `ClientRequest::set_header`; use `ClientRequest::insert_header`. [#1869] +- `ClientRequest::set_header_if_none`; use `ClientRequest::insert_header_if_none`. [#1869] +- `ClientRequest::header`; use `ClientRequest::append_header`. [#1869] [#1869]: https://github.com/actix/actix-web/pull/1869 [#1905]: https://github.com/actix/actix-web/pull/1905 @@ -120,32 +120,32 @@ ## 3.0.0-beta.1 - 2021-01-07 ### Changed -* Update `rand` to `0.8` -* Update `bytes` to `1.0`. [#1813] -* Update `rust-tls` to `0.19`. [#1813] +- Update `rand` to `0.8` +- Update `bytes` to `1.0`. [#1813] +- Update `rust-tls` to `0.19`. [#1813] [#1813]: https://github.com/actix/actix-web/pull/1813 ## 2.0.3 - 2020-11-29 ### Fixed -* Ensure `actix-http` dependency uses same `serde_urlencoded`. +- Ensure `actix-http` dependency uses same `serde_urlencoded`. ## 2.0.2 - 2020-11-25 ### Changed -* Upgrade `serde_urlencoded` to `0.7`. [#1773] +- Upgrade `serde_urlencoded` to `0.7`. [#1773] [#1773]: https://github.com/actix/actix-web/pull/1773 ## 2.0.1 - 2020-10-30 ### Changed -* Upgrade `base64` to `0.13`. [#1744] -* Deprecate `ClientRequest::{if_some, if_true}`. [#1760] +- Upgrade `base64` to `0.13`. [#1744] +- Deprecate `ClientRequest::{if_some, if_true}`. [#1760] ### Fixed -* Use `Accept-Encoding: identity` instead of `Accept-Encoding: br` when no compression feature +- Use `Accept-Encoding: identity` instead of `Accept-Encoding: br` when no compression feature is enabled [#1737] [#1737]: https://github.com/actix/actix-web/pull/1737 @@ -155,209 +155,209 @@ ## 2.0.0 - 2020-09-11 ### Changed -* `Client::build` was renamed to `Client::builder`. +- `Client::build` was renamed to `Client::builder`. ## 2.0.0-beta.4 - 2020-09-09 ### Changed -* Update actix-codec & actix-tls dependencies. +- Update actix-codec & actix-tls dependencies. ## 2.0.0-beta.3 - 2020-08-17 ### Changed -* Update `rustls` to 0.18 +- Update `rustls` to 0.18 ## 2.0.0-beta.2 - 2020-07-21 ### Changed -* Update `actix-http` dependency to 2.0.0-beta.2 +- Update `actix-http` dependency to 2.0.0-beta.2 ## [2.0.0-beta.1] - 2020-07-14 ### Changed -* Update `actix-http` dependency to 2.0.0-beta.1 +- Update `actix-http` dependency to 2.0.0-beta.1 ## [2.0.0-alpha.2] - 2020-05-21 ### Changed -* Implement `std::error::Error` for our custom errors [#1422] -* Bump minimum supported Rust version to 1.40 -* Update `base64` dependency to 0.12 +- Implement `std::error::Error` for our custom errors [#1422] +- Bump minimum supported Rust version to 1.40 +- Update `base64` dependency to 0.12 [#1422]: https://github.com/actix/actix-web/pull/1422 ## [2.0.0-alpha.1] - 2020-03-11 -* Update `actix-http` dependency to 2.0.0-alpha.2 -* Update `rustls` dependency to 0.17 -* ClientBuilder accepts initial_window_size and initial_connection_window_size HTTP2 configuration -* ClientBuilder allowing to set max_http_version to limit HTTP version to be used +- Update `actix-http` dependency to 2.0.0-alpha.2 +- Update `rustls` dependency to 0.17 +- ClientBuilder accepts initial_window_size and initial_connection_window_size HTTP2 configuration +- ClientBuilder allowing to set max_http_version to limit HTTP version to be used ## [1.0.1] - 2019-12-15 -* Fix compilation with default features off +- Fix compilation with default features off ## [1.0.0] - 2019-12-13 -* Release +- Release ## [1.0.0-alpha.3] -* Migrate to `std::future` +- Migrate to `std::future` ## [0.2.8] - 2019-11-06 -* Add support for setting query from Serialize type for client request. +- Add support for setting query from Serialize type for client request. ## [0.2.7] - 2019-09-25 ### Added -* Remaining getter methods for `ClientRequest`'s private `head` field #1101 +- Remaining getter methods for `ClientRequest`'s private `head` field #1101 ## [0.2.6] - 2019-09-12 ### Added -* Export frozen request related types. +- Export frozen request related types. ## [0.2.5] - 2019-09-11 ### Added -* Add `FrozenClientRequest` to support retries for sending HTTP requests +- Add `FrozenClientRequest` to support retries for sending HTTP requests ### Changed -* Ensure that the `Host` header is set when initiating a WebSocket client connection. +- Ensure that the `Host` header is set when initiating a WebSocket client connection. ## [0.2.4] - 2019-08-13 ### Changed -* Update percent-encoding to "2.1" +- Update percent-encoding to "2.1" -* Update serde_urlencoded to "0.6.1" +- Update serde_urlencoded to "0.6.1" ## [0.2.3] - 2019-08-01 ### Added -* Add `rustls` support +- Add `rustls` support ## [0.2.2] - 2019-07-01 ### Changed -* Always append a colon after username in basic auth +- Always append a colon after username in basic auth -* Upgrade `rand` dependency version to 0.7 +- Upgrade `rand` dependency version to 0.7 ## [0.2.1] - 2019-06-05 ### Added -* Add license files +- Add license files ## [0.2.0] - 2019-05-12 ### Added -* Allow to send headers in `Camel-Case` form. +- Allow to send headers in `Camel-Case` form. ### Changed -* Upgrade actix-http dependency. +- Upgrade actix-http dependency. ## [0.1.1] - 2019-04-19 ### Added -* Allow to specify server address for http and ws requests. +- Allow to specify server address for http and ws requests. ### Changed -* `ClientRequest::if_true()` and `ClientRequest::if_some()` use instance instead of ref +- `ClientRequest::if_true()` and `ClientRequest::if_some()` use instance instead of ref ## [0.1.0] - 2019-04-16 -* No changes +- No changes ## [0.1.0-alpha.6] - 2019-04-14 ### Changed -* Do not set default headers for websocket request +- Do not set default headers for websocket request ## [0.1.0-alpha.5] - 2019-04-12 ### Changed -* Do not set any default headers +- Do not set any default headers ### Added -* Add Debug impl for BoxedSocket +- Add Debug impl for BoxedSocket ## [0.1.0-alpha.4] - 2019-04-08 ### Changed -* Update actix-http dependency +- Update actix-http dependency ## [0.1.0-alpha.3] - 2019-04-02 ### Added -* Export `MessageBody` type +- Export `MessageBody` type -* `ClientResponse::json()` - Loads and parse `application/json` encoded body +- `ClientResponse::json()` - Loads and parse `application/json` encoded body ### Changed -* `ClientRequest::json()` accepts reference instead of object. +- `ClientRequest::json()` accepts reference instead of object. -* `ClientResponse::body()` does not consume response object. +- `ClientResponse::body()` does not consume response object. -* Renamed `ClientRequest::close_connection()` to `ClientRequest::force_close()` +- Renamed `ClientRequest::close_connection()` to `ClientRequest::force_close()` ## [0.1.0-alpha.2] - 2019-03-29 ### Added -* Per request and session wide request timeout. +- Per request and session wide request timeout. -* Session wide headers. +- Session wide headers. -* Session wide basic and bearer auth. +- Session wide basic and bearer auth. -* Re-export `actix_http::client::Connector`. +- Re-export `actix_http::client::Connector`. ### Changed -* Allow to override request's uri +- Allow to override request's uri -* Export `ws` sub-module with websockets related types +- Export `ws` sub-module with websockets related types ## [0.1.0-alpha.1] - 2019-03-28 -* Initial impl +- Initial impl From b3ac918d7001a351ecec84317b053e862b6c561c Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 22 Dec 2021 08:34:48 +0000 Subject: [PATCH 63/87] update itoa to v1 --- Cargo.toml | 2 +- actix-http/Cargo.toml | 2 +- actix-http/src/header/shared/quality.rs | 18 ++++++++++++------ awc/Cargo.toml | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 02bef3af6..d15f26172 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,7 +89,7 @@ derive_more = "0.99.5" encoding_rs = "0.8" futures-core = { version = "0.3.7", default-features = false } futures-util = { version = "0.3.7", default-features = false } -itoa = "0.4" +itoa = "1" language-tags = "0.3" once_cell = "1.5" log = "0.4" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 9f93bf6d2..e4eadd37c 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -60,7 +60,7 @@ h2 = "0.3.9" http = "0.2.5" httparse = "1.5.1" httpdate = "1.0.1" -itoa = "0.4" +itoa = "1" language-tags = "0.3" local-channel = "0.1" log = "0.4" diff --git a/actix-http/src/header/shared/quality.rs b/actix-http/src/header/shared/quality.rs index 5321c754d..c2f08edc2 100644 --- a/actix-http/src/header/shared/quality.rs +++ b/actix-http/src/header/shared/quality.rs @@ -87,7 +87,7 @@ impl fmt::Display for Quality { // 0 is already handled so it's not possible to have a trailing 0 in this range // we can just write the integer - itoa::fmt(f, x) + itoa_fmt(f, x) } else if x < 100 { // x in is range 10–99 @@ -95,21 +95,21 @@ impl fmt::Display for Quality { if x % 10 == 0 { // trailing 0, divide by 10 and write - itoa::fmt(f, x / 10) + itoa_fmt(f, x / 10) } else { - itoa::fmt(f, x) + itoa_fmt(f, x) } } else { // x is in range 100–999 if x % 100 == 0 { // two trailing 0s, divide by 100 and write - itoa::fmt(f, x / 100) + itoa_fmt(f, x / 100) } else if x % 10 == 0 { // one trailing 0, divide by 10 and write - itoa::fmt(f, x / 10) + itoa_fmt(f, x / 10) } else { - itoa::fmt(f, x) + itoa_fmt(f, x) } } } @@ -117,6 +117,12 @@ impl fmt::Display for Quality { } } +/// Write integer to a `fmt::Write`. +pub fn itoa_fmt(mut wr: W, value: V) -> fmt::Result { + let mut buf = itoa::Buffer::new(); + wr.write_str(buf.format(value)) +} + #[derive(Debug, Clone, Display, Error)] #[display(fmt = "quality out of bounds")] #[non_exhaustive] diff --git a/awc/Cargo.toml b/awc/Cargo.toml index f9a541c7e..cf12f2383 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -74,7 +74,7 @@ futures-core = { version = "0.3.7", default-features = false, features = ["alloc futures-util = { version = "0.3.7", default-features = false, features = ["alloc", "sink"] } h2 = "0.3.9" http = "0.2.5" -itoa = "0.4" +itoa = "1" log =" 0.4" mime = "0.3" percent-encoding = "2.1" From 324eba7e0b5a0451025a61828da89dbb20f17966 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 22 Dec 2021 08:41:44 +0000 Subject: [PATCH 64/87] tighten tokio version range to prevent RUSTSEC-2021-0124 --- actix-http-test/Cargo.toml | 2 +- actix-http/Cargo.toml | 2 +- actix-multipart/Cargo.toml | 2 +- actix-test/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index 0c205fc2a..e1c875a1f 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -48,7 +48,7 @@ serde_json = "1.0" slab = "0.4" serde_urlencoded = "0.7" tls-openssl = { version = "0.10.9", package = "openssl", optional = true } -tokio = { version = "1.2", features = ["sync"] } +tokio = { version = "1.8", features = ["sync"] } [dev-dependencies] actix-web = { version = "4.0.0-beta.15", default-features = false, features = ["cookies"] } diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index e4eadd37c..3ad3d786e 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -97,7 +97,7 @@ serde_json = "1.0" static_assertions = "1" tls-openssl = { package = "openssl", version = "0.10.9" } tls-rustls = { package = "rustls", version = "0.20.0" } -tokio = { version = "1.2", features = ["net", "rt", "macros"] } +tokio = { version = "1.8", features = ["net", "rt", "macros"] } [[example]] name = "ws" diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index c7145e542..595c14d7e 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -30,5 +30,5 @@ twoway = "0.2" actix-rt = "2.2" actix-http = "3.0.0-beta.16" futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] } -tokio = { version = "1", features = ["sync"] } +tokio = { version = "1.8", features = ["sync"] } tokio-stream = "0.1" diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index 7957b3a9c..4a4615820 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -45,4 +45,4 @@ serde_json = "1" serde_urlencoded = "0.7" tls-openssl = { package = "openssl", version = "0.10.9", optional = true } tls-rustls = { package = "rustls", version = "0.20.0", optional = true } -tokio = { version = "1.2", features = ["sync"] } +tokio = { version = "1.8", features = ["sync"] } diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 3f213f378..d57f139f6 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -23,7 +23,7 @@ bytes = "1" bytestring = "1" futures-core = { version = "0.3.7", default-features = false } pin-project-lite = "0.2" -tokio = { version = "1", features = ["sync"] } +tokio = { version = "1.8", features = ["sync"] } [dev-dependencies] actix-rt = "2.2" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index cf12f2383..4b29aac16 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -83,7 +83,7 @@ rand = "0.8" serde = "1.0" serde_json = "1.0" serde_urlencoded = "0.7" -tokio = { version = "1", features = ["sync"] } +tokio = { version = "1.8", features = ["sync"] } cookie = { version = "0.15", features = ["percent-encode"], optional = true } From 1769812d0b19cd9df59c3c60b4c35f495bee5d1b Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 22 Dec 2021 08:43:38 +0000 Subject: [PATCH 65/87] bump outdated deps --- actix-http/Cargo.toml | 2 +- actix-router/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 3ad3d786e..2958a1c77 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -68,7 +68,7 @@ mime = "0.3" percent-encoding = "2.1" pin-project-lite = "0.2" rand = "0.8" -sha-1 = "0.9" +sha-1 = "0.10" smallvec = "1.6.1" # tls diff --git a/actix-router/Cargo.toml b/actix-router/Cargo.toml index afd39dfd3..c63448bc7 100644 --- a/actix-router/Cargo.toml +++ b/actix-router/Cargo.toml @@ -21,7 +21,7 @@ default = ["http"] [dependencies] bytestring = ">=0.1.5, <2" -firestorm = "0.4" +firestorm = "0.5" http = { version = "0.2.3", optional = true } log = "0.4" regex = "1.5" @@ -29,7 +29,7 @@ serde = "1" [dev-dependencies] criterion = { version = "0.3", features = ["html_reports"] } -firestorm = { version = "0.4", features = ["enable_system_time"] } +firestorm = { version = "0.5", features = ["enable_system_time"] } http = "0.2.5" serde = { version = "1", features = ["derive"] } From cd025f5c0ba7774263cbae38fd6b4c652b4d54a3 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 22 Dec 2021 15:00:32 +0000 Subject: [PATCH 66/87] allow any body type in Resource (#2526) --- CHANGES.md | 4 +++ actix-http/Cargo.toml | 1 - src/middleware/compat.rs | 9 +++++ src/middleware/mod.rs | 4 +++ src/middleware/noop.rs | 37 +++++++++++++++++++++ src/resource.rs | 72 +++++++++++++++++++++++++++++----------- src/scope.rs | 10 +++--- 7 files changed, 112 insertions(+), 25 deletions(-) create mode 100644 src/middleware/noop.rs diff --git a/CHANGES.md b/CHANGES.md index 8e030819f..07c247554 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,10 @@ # Changes ## Unreleased - 2021-xx-xx +### Changed +- No longer require `Resource` service body type to be boxed. [#2526] + +[#2526]: https://github.com/actix/actix-web/pull/2526 ## 4.0.0-beta.15 - 2021-12-17 diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 2958a1c77..c15f5ee28 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -55,7 +55,6 @@ bytestring = "1" derive_more = "0.99.5" encoding_rs = "0.8" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } -futures-task = { version = "0.3.7", default-features = false, features = ["alloc"] } h2 = "0.3.9" http = "0.2.5" httparse = "1.5.1" diff --git a/src/middleware/compat.rs b/src/middleware/compat.rs index d49c461c4..3386240b7 100644 --- a/src/middleware/compat.rs +++ b/src/middleware/compat.rs @@ -38,6 +38,15 @@ pub struct Compat { transform: T, } +#[cfg(test)] +impl Compat { + pub(crate) fn noop() -> Self { + Self { + transform: super::Noop, + } + } +} + impl Compat { /// Wrap a middleware to give it broader compatibility. pub fn new(middleware: T) -> Self { diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 0da9b9b2e..a781052a6 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -5,6 +5,8 @@ mod condition; mod default_headers; mod err_handlers; mod logger; +#[cfg(test)] +mod noop; mod normalize; pub use self::compat::Compat; @@ -12,6 +14,8 @@ pub use self::condition::Condition; pub use self::default_headers::DefaultHeaders; pub use self::err_handlers::{ErrorHandlerResponse, ErrorHandlers}; pub use self::logger::Logger; +#[cfg(test)] +pub(crate) use self::noop::Noop; pub use self::normalize::{NormalizePath, TrailingSlash}; #[cfg(feature = "__compress")] diff --git a/src/middleware/noop.rs b/src/middleware/noop.rs new file mode 100644 index 000000000..ae7da1d81 --- /dev/null +++ b/src/middleware/noop.rs @@ -0,0 +1,37 @@ +//! A no-op middleware. See [Noop] for docs. + +use actix_utils::future::{ready, Ready}; + +use crate::dev::{Service, Transform}; + +/// A no-op middleware that passes through request and response untouched. +pub(crate) struct Noop; + +impl, Req> Transform for Noop { + type Response = S::Response; + type Error = S::Error; + type Transform = NoopService; + type InitError = (); + type Future = Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + ready(Ok(NoopService { service })) + } +} + +#[doc(hidden)] +pub(crate) struct NoopService { + service: S, +} + +impl, Req> Service for NoopService { + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + crate::dev::forward_ready!(service); + + fn call(&self, req: Req) -> Self::Future { + self.service.call(req) + } +} diff --git a/src/resource.rs b/src/resource.rs index c13544063..d94d2a464 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -1,6 +1,6 @@ -use std::{cell::RefCell, fmt, future::Future, rc::Rc}; +use std::{cell::RefCell, fmt, future::Future, marker::PhantomData, rc::Rc}; -use actix_http::Extensions; +use actix_http::{body::BoxBody, Extensions}; use actix_router::{IntoPatterns, Patterns}; use actix_service::{ apply, apply_fn_factory, boxed, fn_service, IntoServiceFactory, Service, ServiceFactory, @@ -45,7 +45,7 @@ use crate::{ /// /// If no matching route could be found, *405* response code get returned. /// Default behavior could be overridden with `default_resource()` method. -pub struct Resource { +pub struct Resource { endpoint: T, rdef: Patterns, name: Option, @@ -54,6 +54,7 @@ pub struct Resource { guards: Vec>, default: BoxedHttpServiceFactory, factory_ref: Rc>>, + _phantom: PhantomData, } impl Resource { @@ -71,19 +72,21 @@ impl Resource { default: boxed::factory(fn_service(|req: ServiceRequest| async { Ok(req.into_response(HttpResponse::MethodNotAllowed())) })), + _phantom: PhantomData, } } } -impl Resource +impl Resource where T: ServiceFactory< ServiceRequest, Config = (), - Response = ServiceResponse, + Response = ServiceResponse, Error = Error, InitError = (), >, + B: MessageBody, { /// Set resource name. /// @@ -252,26 +255,28 @@ where /// type (i.e modify response's body). /// /// **Note**: middlewares get called in opposite order of middlewares registration. - pub fn wrap( + pub fn wrap( self, mw: M, ) -> Resource< impl ServiceFactory< ServiceRequest, Config = (), - Response = ServiceResponse, + Response = ServiceResponse, Error = Error, InitError = (), >, + B1, > where M: Transform< T::Service, ServiceRequest, - Response = ServiceResponse, + Response = ServiceResponse, Error = Error, InitError = (), >, + B1: MessageBody, { Resource { endpoint: apply(mw, self.endpoint), @@ -282,6 +287,7 @@ where default: self.default, app_data: self.app_data, factory_ref: self.factory_ref, + _phantom: PhantomData, } } @@ -319,21 +325,23 @@ where /// .route(web::get().to(index))); /// } /// ``` - pub fn wrap_fn( + pub fn wrap_fn( self, mw: F, ) -> Resource< impl ServiceFactory< ServiceRequest, Config = (), - Response = ServiceResponse, + Response = ServiceResponse, Error = Error, InitError = (), >, + B1, > where F: Fn(ServiceRequest, &T::Service) -> R + Clone, - R: Future>, + R: Future, Error>>, + B1: MessageBody, { Resource { endpoint: apply_fn_factory(self.endpoint, mw), @@ -344,6 +352,7 @@ where default: self.default, app_data: self.app_data, factory_ref: self.factory_ref, + _phantom: PhantomData, } } @@ -371,15 +380,16 @@ where } } -impl HttpServiceFactory for Resource +impl HttpServiceFactory for Resource where T: ServiceFactory< ServiceRequest, Config = (), - Response = ServiceResponse, + Response = ServiceResponse, Error = Error, InitError = (), > + 'static, + B: MessageBody + 'static, { fn register(mut self, config: &mut AppService) { let guards = if self.guards.is_empty() { @@ -411,7 +421,9 @@ where req.add_data_container(Rc::clone(data)); } - srv.call(req) + let fut = srv.call(req); + + async { Ok(fut.await?.map_into_boxed_body()) } }); config.register_service(rdef, guards, endpoint, None) @@ -534,11 +546,11 @@ mod tests { >, > { web::resource("/test-compat") - // .wrap_fn(|req, srv| { - // let fut = srv.call(req); - // async { Ok(fut.await?.map_into_right_body::<()>()) } - // }) - .wrap(Compat::new(DefaultHeaders::new())) + .wrap_fn(|req, srv| { + let fut = srv.call(req); + async { Ok(fut.await?.map_into_right_body::<()>()) } + }) + .wrap(Compat::noop()) .route(web::get().to(|| async { "hello" })) } @@ -801,4 +813,26 @@ mod tests { let resp = call_service(&srv, req).await; assert_eq!(resp.status(), StatusCode::BAD_REQUEST); } + + #[actix_rt::test] + async fn test_middleware_body_type() { + let srv = init_service( + App::new().service( + web::resource("/test") + .wrap_fn(|req, srv| { + let fut = srv.call(req); + async { Ok(fut.await?.map_into_right_body::<()>()) } + }) + .route(web::get().to(|| async { "hello" })), + ), + ) + .await; + + // test if `try_into_bytes()` is preserved across scope layer + use actix_http::body::MessageBody as _; + let req = TestRequest::with_uri("/test").to_request(); + let resp = call_service(&srv, req).await; + let body = resp.into_body(); + assert_eq!(body.try_into_bytes().unwrap(), b"hello".as_ref()); + } } diff --git a/src/scope.rs b/src/scope.rs index 35bbb50ba..7f9a94875 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -616,11 +616,11 @@ mod tests { >, > { web::scope("/test-compat") - // .wrap_fn(|req, srv| { - // let fut = srv.call(req); - // async { Ok(fut.await?.map_into_right_body::<()>()) } - // }) - .wrap(Compat::new(DefaultHeaders::new())) + .wrap_fn(|req, srv| { + let fut = srv.call(req); + async { Ok(fut.await?.map_into_right_body::<()>()) } + }) + .wrap(Compat::noop()) .service(web::resource("").route(web::get().to(|| async { "hello" }))) } From 7b1512d863ed63cb7a1fab51ff358b24b33c5f19 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Wed, 22 Dec 2021 18:48:59 +0300 Subject: [PATCH 67/87] allow any body type in Scope (#2523) --- CHANGES.md | 2 ++ src/middleware/compat.rs | 2 +- src/scope.rs | 37 +++++++++++++++++++++++++++++++++---- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 07c247554..a43b3ee41 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,8 +2,10 @@ ## Unreleased - 2021-xx-xx ### Changed +- No longer require `Scope` service body type to be boxed. [#2523] - No longer require `Resource` service body type to be boxed. [#2526] +[#2523]: https://github.com/actix/actix-web/pull/2523 [#2526]: https://github.com/actix/actix-web/pull/2526 diff --git a/src/middleware/compat.rs b/src/middleware/compat.rs index 3386240b7..18c9ff6a7 100644 --- a/src/middleware/compat.rs +++ b/src/middleware/compat.rs @@ -17,7 +17,7 @@ use crate::{ }; /// Middleware for enabling any middleware to be used in [`Resource::wrap`](crate::Resource::wrap), -/// [`Scope::wrap`](crate::Scope::wrap) and [`Condition`](super::Condition). +/// and [`Condition`](super::Condition). /// /// # Examples /// ``` diff --git a/src/scope.rs b/src/scope.rs index 7f9a94875..1fd282f61 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,6 +1,9 @@ use std::{cell::RefCell, fmt, future::Future, marker::PhantomData, mem, rc::Rc}; -use actix_http::{body::BoxBody, Extensions}; +use actix_http::{ + body::{BoxBody, MessageBody}, + Extensions, +}; use actix_router::{ResourceDef, Router}; use actix_service::{ apply, apply_fn_factory, boxed, IntoServiceFactory, Service, ServiceFactory, @@ -399,15 +402,16 @@ where } } -impl HttpServiceFactory for Scope +impl HttpServiceFactory for Scope where T: ServiceFactory< ServiceRequest, Config = (), - Response = ServiceResponse, + Response = ServiceResponse, Error = Error, InitError = (), > + 'static, + B: MessageBody + 'static, { fn register(mut self, config: &mut AppService) { // update default resource if needed @@ -457,7 +461,9 @@ where req.add_data_container(Rc::clone(data)); } - srv.call(req) + let fut = srv.call(req); + + async { Ok(fut.await?.map_into_boxed_body()) } }); // register final service @@ -980,6 +986,29 @@ mod tests { ); } + #[actix_rt::test] + async fn test_middleware_body_type() { + // Compile test that Scope accepts any body type; test for `EitherBody` + let srv = init_service( + App::new().service( + web::scope("app") + .wrap_fn(|req, srv| { + let fut = srv.call(req); + async { Ok(fut.await?.map_into_right_body::<()>()) } + }) + .service(web::resource("/test").route(web::get().to(|| async { "hello" }))), + ), + ) + .await; + + // test if `MessageBody::try_into_bytes()` is preserved across scope layer + use actix_http::body::MessageBody as _; + let req = TestRequest::with_uri("/app/test").to_request(); + let resp = call_service(&srv, req).await; + let body = resp.into_body(); + assert_eq!(body.try_into_bytes().unwrap(), b"hello".as_ref()); + } + #[actix_rt::test] async fn test_middleware_fn() { let srv = init_service( From 1296e07c4830f0ab2e2864a6fc9faa93972e5935 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 24 Dec 2021 17:47:47 +0000 Subject: [PATCH 68/87] relax unpin bounds on payload types (#2545) --- actix-http/CHANGES.md | 9 ++++ actix-http/src/encoding/decoder.rs | 39 ++++++++------- actix-http/src/h1/dispatcher.rs | 7 +-- actix-http/src/h1/payload.rs | 76 +++++++++++++++++------------- actix-http/src/h1/utils.rs | 4 +- actix-http/src/h2/dispatcher.rs | 4 +- actix-http/src/h2/mod.rs | 11 +++++ actix-http/src/lib.rs | 3 +- actix-http/src/payload.rs | 73 +++++++++++++++++----------- actix-http/src/requests/request.rs | 11 +++-- actix-http/src/test.rs | 2 +- actix-http/tests/test_rustls.rs | 33 +++++++++---- actix-multipart/src/server.rs | 2 +- awc/src/client/connection.rs | 4 +- awc/src/client/h1proto.rs | 16 +++++-- awc/src/response.rs | 6 +-- awc/src/sender.rs | 15 +++--- awc/src/test.rs | 5 +- src/dev.rs | 2 +- src/response/builder.rs | 36 ++++++-------- src/service.rs | 4 +- src/test/test_request.rs | 21 +++++---- 22 files changed, 229 insertions(+), 154 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 3b45e934f..adc4c35c7 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -3,8 +3,17 @@ ## Unreleased - 2021-xx-xx ### Changes - `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527] +- `Payload` inner fields are now named. [#2545] +- `impl Stream` for `Payload` no longer requires the `Stream` variant be `Unpin`. [#2545] +- `impl Future` for `h1::SendResponse` no longer requires the body type be `Unpin`. [#2545] +- `impl Stream` for `encoding::Decoder` no longer requires the stream type be `Unpin`. [#2545] +- Rename `PayloadStream` to `BoxedPayloadStream`. [#2545] + +### Removed +- `h1::Payload::readany`. [#2545] [#2527]: https://github.com/actix/actix-web/pull/2527 +[#2545]: https://github.com/actix/actix-web/pull/2545 ## 3.0.0-beta.16 - 2021-12-17 diff --git a/actix-http/src/encoding/decoder.rs b/actix-http/src/encoding/decoder.rs index a46e330c9..0f519637a 100644 --- a/actix-http/src/encoding/decoder.rs +++ b/actix-http/src/encoding/decoder.rs @@ -28,11 +28,14 @@ use crate::{ const MAX_CHUNK_SIZE_DECODE_IN_PLACE: usize = 2049; -pub struct Decoder { - decoder: Option, - stream: S, - eof: bool, - fut: Option, ContentDecoder), io::Error>>>, +pin_project_lite::pin_project! { + pub struct Decoder { + decoder: Option, + #[pin] + stream: S, + eof: bool, + fut: Option, ContentDecoder), io::Error>>>, + } } impl Decoder @@ -89,42 +92,44 @@ where impl Stream for Decoder where - S: Stream> + Unpin, + S: Stream>, { type Item = Result; - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + loop { - if let Some(ref mut fut) = self.fut { + if let Some(ref mut fut) = this.fut { let (chunk, decoder) = ready!(Pin::new(fut).poll(cx)).map_err(|_| BlockingError)??; - self.decoder = Some(decoder); - self.fut.take(); + *this.decoder = Some(decoder); + this.fut.take(); if let Some(chunk) = chunk { return Poll::Ready(Some(Ok(chunk))); } } - if self.eof { + if *this.eof { return Poll::Ready(None); } - match ready!(Pin::new(&mut self.stream).poll_next(cx)) { + match ready!(this.stream.as_mut().poll_next(cx)) { Some(Err(err)) => return Poll::Ready(Some(Err(err))), Some(Ok(chunk)) => { - if let Some(mut decoder) = self.decoder.take() { + if let Some(mut decoder) = this.decoder.take() { if chunk.len() < MAX_CHUNK_SIZE_DECODE_IN_PLACE { let chunk = decoder.feed_data(chunk)?; - self.decoder = Some(decoder); + *this.decoder = Some(decoder); if let Some(chunk) = chunk { return Poll::Ready(Some(Ok(chunk))); } } else { - self.fut = Some(spawn_blocking(move || { + *this.fut = Some(spawn_blocking(move || { let chunk = decoder.feed_data(chunk)?; Ok((chunk, decoder)) })); @@ -137,9 +142,9 @@ where } None => { - self.eof = true; + *this.eof = true; - return if let Some(mut decoder) = self.decoder.take() { + return if let Some(mut decoder) = this.decoder.take() { match decoder.feed_eof() { Ok(Some(res)) => Poll::Ready(Some(Ok(res))), Ok(None) => Poll::Ready(None), diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 5c0cb64af..13055f08a 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -646,10 +646,11 @@ where Payload is attached to Request and passed to Service::call where the state can be collected and consumed. */ - let (ps, pl) = Payload::create(false); - let (req1, _) = req.replace_payload(crate::Payload::H1(pl)); + let (sender, payload) = Payload::create(false); + let (req1, _) = + req.replace_payload(crate::Payload::H1 { payload }); req = req1; - *this.payload = Some(ps); + *this.payload = Some(sender); } // Request has no payload. diff --git a/actix-http/src/h1/payload.rs b/actix-http/src/h1/payload.rs index f912e0ba3..4d031c15a 100644 --- a/actix-http/src/h1/payload.rs +++ b/actix-http/src/h1/payload.rs @@ -1,9 +1,12 @@ //! Payload stream -use std::cell::RefCell; -use std::collections::VecDeque; -use std::pin::Pin; -use std::rc::{Rc, Weak}; -use std::task::{Context, Poll, Waker}; + +use std::{ + cell::RefCell, + collections::VecDeque, + pin::Pin, + rc::{Rc, Weak}, + task::{Context, Poll, Waker}, +}; use bytes::Bytes; use futures_core::Stream; @@ -22,39 +25,32 @@ pub enum PayloadStatus { /// Buffered stream of bytes chunks /// -/// Payload stores chunks in a vector. First chunk can be received with -/// `.readany()` method. Payload stream is not thread safe. Payload does not -/// notify current task when new data is available. +/// Payload stores chunks in a vector. First chunk can be received with `poll_next`. Payload does +/// not notify current task when new data is available. /// -/// Payload stream can be used as `Response` body stream. +/// Payload can be used as `Response` body stream. #[derive(Debug)] pub struct Payload { inner: Rc>, } impl Payload { - /// Create payload stream. + /// Creates a payload stream. /// - /// This method construct two objects responsible for bytes stream - /// generation. - /// - /// * `PayloadSender` - *Sender* side of the stream - /// - /// * `Payload` - *Receiver* side of the stream + /// This method construct two objects responsible for bytes stream generation: + /// - `PayloadSender` - *Sender* side of the stream + /// - `Payload` - *Receiver* side of the stream pub fn create(eof: bool) -> (PayloadSender, Payload) { let shared = Rc::new(RefCell::new(Inner::new(eof))); ( - PayloadSender { - inner: Rc::downgrade(&shared), - }, + PayloadSender::new(Rc::downgrade(&shared)), Payload { inner: shared }, ) } - /// Create empty payload - #[doc(hidden)] - pub fn empty() -> Payload { + /// Creates an empty payload. + pub(crate) fn empty() -> Payload { Payload { inner: Rc::new(RefCell::new(Inner::new(true))), } @@ -77,14 +73,6 @@ impl Payload { pub fn unread_data(&mut self, data: Bytes) { self.inner.borrow_mut().unread_data(data); } - - #[inline] - pub fn readany( - &mut self, - cx: &mut Context<'_>, - ) -> Poll>> { - self.inner.borrow_mut().readany(cx) - } } impl Stream for Payload { @@ -94,7 +82,7 @@ impl Stream for Payload { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { - self.inner.borrow_mut().readany(cx) + Pin::new(&mut *self.inner.borrow_mut()).poll_next(cx) } } @@ -104,6 +92,10 @@ pub struct PayloadSender { } impl PayloadSender { + fn new(inner: Weak>) -> Self { + Self { inner } + } + #[inline] pub fn set_error(&mut self, err: PayloadError) { if let Some(shared) = self.inner.upgrade() { @@ -227,7 +219,10 @@ impl Inner { self.len } - fn readany(&mut self, cx: &mut Context<'_>) -> Poll>> { + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { if let Some(data) = self.items.pop_front() { self.len -= data.len(); self.need_read = self.len < MAX_BUFFER_SIZE; @@ -257,8 +252,18 @@ impl Inner { #[cfg(test)] mod tests { - use super::*; + use std::panic::{RefUnwindSafe, UnwindSafe}; + use actix_utils::future::poll_fn; + use static_assertions::{assert_impl_all, assert_not_impl_any}; + + use super::*; + + assert_impl_all!(Payload: Unpin); + assert_not_impl_any!(Payload: Send, Sync, UnwindSafe, RefUnwindSafe); + + assert_impl_all!(Inner: Unpin, Send, Sync); + assert_not_impl_any!(Inner: UnwindSafe, RefUnwindSafe); #[actix_rt::test] async fn test_unread_data() { @@ -270,7 +275,10 @@ mod tests { assert_eq!( Bytes::from("data"), - poll_fn(|cx| payload.readany(cx)).await.unwrap().unwrap() + poll_fn(|cx| Pin::new(&mut payload).poll_next(cx)) + .await + .unwrap() + .unwrap() ); } } diff --git a/actix-http/src/h1/utils.rs b/actix-http/src/h1/utils.rs index 131c7f1ed..5c11b1dab 100644 --- a/actix-http/src/h1/utils.rs +++ b/actix-http/src/h1/utils.rs @@ -45,7 +45,7 @@ where impl Future for SendResponse where T: AsyncRead + AsyncWrite + Unpin, - B: MessageBody + Unpin, + B: MessageBody, B::Error: Into, { type Output = Result, Error>; @@ -81,7 +81,7 @@ where // body is done when item is None body_done = item.is_none(); if body_done { - let _ = this.body.take(); + this.body.set(None); } let framed = this.framed.as_mut().as_pin_mut().unwrap(); framed diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 8fbefe6de..7f0f15ee6 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -108,8 +108,8 @@ where match Pin::new(&mut this.connection).poll_accept(cx)? { Poll::Ready(Some((req, tx))) => { let (parts, body) = req.into_parts(); - let pl = crate::h2::Payload::new(body); - let pl = Payload::H2(pl); + let payload = crate::h2::Payload::new(body); + let pl = Payload::H2 { payload }; let mut req = Request::with_payload(pl); let head = req.head_mut(); diff --git a/actix-http/src/h2/mod.rs b/actix-http/src/h2/mod.rs index cbcb6d0fc..47d51b420 100644 --- a/actix-http/src/h2/mod.rs +++ b/actix-http/src/h2/mod.rs @@ -98,3 +98,14 @@ where } } } + +#[cfg(test)] +mod tests { + use std::panic::{RefUnwindSafe, UnwindSafe}; + + use static_assertions::assert_impl_all; + + use super::*; + + assert_impl_all!(Payload: Unpin, Send, Sync, UnwindSafe, RefUnwindSafe); +} diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index 2b7bc730b..f2b415790 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -58,7 +58,8 @@ pub use self::header::ContentEncoding; pub use self::http_message::HttpMessage; pub use self::message::ConnectionType; pub use self::message::Message; -pub use self::payload::{Payload, PayloadStream}; +#[allow(deprecated)] +pub use self::payload::{BoxedPayloadStream, Payload, PayloadStream}; pub use self::requests::{Request, RequestHead, RequestHeadType}; pub use self::responses::{Response, ResponseBuilder, ResponseHead}; pub use self::service::HttpService; diff --git a/actix-http/src/payload.rs b/actix-http/src/payload.rs index 69840e7c1..c9f338c7d 100644 --- a/actix-http/src/payload.rs +++ b/actix-http/src/payload.rs @@ -1,70 +1,89 @@ use std::{ + mem, pin::Pin, task::{Context, Poll}, }; use bytes::Bytes; use futures_core::Stream; -use h2::RecvStream; use crate::error::PayloadError; -// TODO: rename to boxed payload -/// A boxed payload. -pub type PayloadStream = Pin>>>; +/// A boxed payload stream. +pub type BoxedPayloadStream = Pin>>>; -/// A streaming payload. -pub enum Payload { - None, - H1(crate::h1::Payload), - H2(crate::h2::Payload), - Stream(S), +#[deprecated(since = "4.0.0", note = "Renamed to `BoxedPayloadStream`.")] +pub type PayloadStream = BoxedPayloadStream; + +pin_project_lite::pin_project! { + /// A streaming payload. + #[project = PayloadProj] + pub enum Payload { + None, + H1 { payload: crate::h1::Payload }, + H2 { payload: crate::h2::Payload }, + Stream { #[pin] payload: S }, + } } impl From for Payload { - fn from(v: crate::h1::Payload) -> Self { - Payload::H1(v) + fn from(payload: crate::h1::Payload) -> Self { + Payload::H1 { payload } } } impl From for Payload { - fn from(v: crate::h2::Payload) -> Self { - Payload::H2(v) + fn from(payload: crate::h2::Payload) -> Self { + Payload::H2 { payload } } } -impl From for Payload { - fn from(v: RecvStream) -> Self { - Payload::H2(crate::h2::Payload::new(v)) +impl From for Payload { + fn from(stream: h2::RecvStream) -> Self { + Payload::H2 { + payload: crate::h2::Payload::new(stream), + } } } -impl From for Payload { - fn from(pl: PayloadStream) -> Self { - Payload::Stream(pl) +impl From for Payload { + fn from(payload: BoxedPayloadStream) -> Self { + Payload::Stream { payload } } } impl Payload { /// Takes current payload and replaces it with `None` value pub fn take(&mut self) -> Payload { - std::mem::replace(self, Payload::None) + mem::replace(self, Payload::None) } } impl Stream for Payload where - S: Stream> + Unpin, + S: Stream>, { type Item = Result; #[inline] fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.get_mut() { - Payload::None => Poll::Ready(None), - Payload::H1(ref mut pl) => pl.readany(cx), - Payload::H2(ref mut pl) => Pin::new(pl).poll_next(cx), - Payload::Stream(ref mut pl) => Pin::new(pl).poll_next(cx), + match self.project() { + PayloadProj::None => Poll::Ready(None), + PayloadProj::H1 { payload } => Pin::new(payload).poll_next(cx), + PayloadProj::H2 { payload } => Pin::new(payload).poll_next(cx), + PayloadProj::Stream { payload } => payload.poll_next(cx), } } } + +#[cfg(test)] +mod tests { + use std::panic::{RefUnwindSafe, UnwindSafe}; + + use static_assertions::{assert_impl_all, assert_not_impl_any}; + + use super::*; + + assert_impl_all!(Payload: Unpin); + assert_not_impl_any!(Payload: Send, Sync, UnwindSafe, RefUnwindSafe); +} diff --git a/actix-http/src/requests/request.rs b/actix-http/src/requests/request.rs index 0254a8f11..4eaaba8e1 100644 --- a/actix-http/src/requests/request.rs +++ b/actix-http/src/requests/request.rs @@ -10,11 +10,12 @@ use std::{ use http::{header, Method, Uri, Version}; use crate::{ - header::HeaderMap, Extensions, HttpMessage, Message, Payload, PayloadStream, RequestHead, + header::HeaderMap, BoxedPayloadStream, Extensions, HttpMessage, Message, Payload, + RequestHead, }; /// An HTTP request. -pub struct Request

{ +pub struct Request

{ pub(crate) payload: Payload

, pub(crate) head: Message, pub(crate) conn_data: Option>, @@ -46,7 +47,7 @@ impl

HttpMessage for Request

{ } } -impl From> for Request { +impl From> for Request { fn from(head: Message) -> Self { Request { head, @@ -57,10 +58,10 @@ impl From> for Request { } } -impl Request { +impl Request { /// Create new Request instance #[allow(clippy::new_without_default)] - pub fn new() -> Request { + pub fn new() -> Request { Request { head: Message::new(), payload: Payload::None, diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs index ea80345fe..1f76498ef 100644 --- a/actix-http/src/test.rs +++ b/actix-http/src/test.rs @@ -120,7 +120,7 @@ impl TestRequest { } /// Set request payload. - pub fn set_payload>(&mut self, data: B) -> &mut Self { + pub fn set_payload(&mut self, data: impl Into) -> &mut Self { let mut payload = crate::h1::Payload::empty(); payload.unread_data(data.into()); parts(&mut self.0).payload = Some(payload.into()); diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index 42ff0dba1..51fefae72 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -7,6 +7,7 @@ use std::{ io::{self, BufReader, Write}, net::{SocketAddr, TcpStream as StdTcpStream}, sync::Arc, + task::Poll, }; use actix_http::{ @@ -16,25 +17,37 @@ use actix_http::{ Error, HttpService, Method, Request, Response, StatusCode, Version, }; use actix_http_test::test_server; +use actix_rt::pin; use actix_service::{fn_factory_with_config, fn_service}; use actix_tls::connect::rustls::webpki_roots_cert_store; -use actix_utils::future::{err, ok}; +use actix_utils::future::{err, ok, poll_fn}; use bytes::{Bytes, BytesMut}; use derive_more::{Display, Error}; -use futures_core::Stream; -use futures_util::stream::{once, StreamExt as _}; +use futures_core::{ready, Stream}; +use futures_util::stream::once; use rustls::{Certificate, PrivateKey, ServerConfig as RustlsServerConfig, ServerName}; use rustls_pemfile::{certs, pkcs8_private_keys}; -async fn load_body(mut stream: S) -> Result +async fn load_body(stream: S) -> Result where - S: Stream> + Unpin, + S: Stream>, { - let mut body = BytesMut::new(); - while let Some(item) = stream.next().await { - body.extend_from_slice(&item?) - } - Ok(body) + let mut buf = BytesMut::new(); + + pin!(stream); + + poll_fn(|cx| loop { + let body = stream.as_mut(); + + match ready!(body.poll_next(cx)) { + Some(Ok(bytes)) => buf.extend_from_slice(&*bytes), + None => return Poll::Ready(Ok(())), + Some(Err(err)) => return Poll::Ready(Err(err)), + } + }) + .await?; + + Ok(buf) } fn tls_config() -> RustlsServerConfig { diff --git a/actix-multipart/src/server.rs b/actix-multipart/src/server.rs index 8eabcee10..239f7f905 100644 --- a/actix-multipart/src/server.rs +++ b/actix-multipart/src/server.rs @@ -1233,7 +1233,7 @@ mod tests { // and should not consume the payload match payload { - actix_web::dev::Payload::H1(_) => {} //expected + actix_web::dev::Payload::H1 { .. } => {} //expected _ => unreachable!(), } } diff --git a/awc/src/client/connection.rs b/awc/src/client/connection.rs index 0e1f0bfec..456f119aa 100644 --- a/awc/src/client/connection.rs +++ b/awc/src/client/connection.rs @@ -267,7 +267,9 @@ where Connection::Tls(ConnectionType::H2(conn)) => { h2proto::send_request(conn, head.into(), body).await } - _ => unreachable!("Plain Tcp connection can be used only in Http1 protocol"), + _ => { + unreachable!("Plain TCP connection can be used only with HTTP/1.1 protocol") + } } }) } diff --git a/awc/src/client/h1proto.rs b/awc/src/client/h1proto.rs index 1028a2178..cf716db72 100644 --- a/awc/src/client/h1proto.rs +++ b/awc/src/client/h1proto.rs @@ -13,16 +13,17 @@ use actix_http::{ Payload, RequestHeadType, ResponseHead, StatusCode, }; use actix_utils::future::poll_fn; -use bytes::buf::BufMut; -use bytes::{Bytes, BytesMut}; +use bytes::{buf::BufMut, Bytes, BytesMut}; use futures_core::{ready, Stream}; use futures_util::SinkExt as _; use pin_project_lite::pin_project; use crate::BoxError; -use super::connection::{ConnectionIo, H1Connection}; -use super::error::{ConnectError, SendRequestError}; +use super::{ + connection::{ConnectionIo, H1Connection}, + error::{ConnectError, SendRequestError}, +}; pub(crate) async fn send_request( io: H1Connection, @@ -123,7 +124,12 @@ where Ok((head, Payload::None)) } - _ => Ok((head, Payload::Stream(Box::pin(PlStream::new(framed))))), + _ => Ok(( + head, + Payload::Stream { + payload: Box::pin(PlStream::new(framed)), + }, + )), } } diff --git a/awc/src/response.rs b/awc/src/response.rs index fefebd0a0..78cc339b4 100644 --- a/awc/src/response.rs +++ b/awc/src/response.rs @@ -10,8 +10,8 @@ use std::{ }; use actix_http::{ - error::PayloadError, header, header::HeaderMap, Extensions, HttpMessage, Payload, - PayloadStream, ResponseHead, StatusCode, Version, + error::PayloadError, header, header::HeaderMap, BoxedPayloadStream, Extensions, + HttpMessage, Payload, ResponseHead, StatusCode, Version, }; use actix_rt::time::{sleep, Sleep}; use bytes::{Bytes, BytesMut}; @@ -23,7 +23,7 @@ use crate::cookie::{Cookie, ParseError as CookieParseError}; use crate::error::JsonPayloadError; /// Client Response -pub struct ClientResponse { +pub struct ClientResponse { pub(crate) head: ResponseHead, pub(crate) payload: Payload, pub(crate) timeout: ResponseTimeout, diff --git a/awc/src/sender.rs b/awc/src/sender.rs index f83a70a9b..29c814531 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -20,7 +20,7 @@ use futures_core::Stream; use serde::Serialize; #[cfg(feature = "__compress")] -use actix_http::{encoding::Decoder, header::ContentEncoding, Payload, PayloadStream}; +use actix_http::{encoding::Decoder, header::ContentEncoding, Payload}; use crate::{ any_body::AnyBody, @@ -91,7 +91,7 @@ impl SendClientRequest { #[cfg(feature = "__compress")] impl Future for SendClientRequest { - type Output = Result>>, SendRequestError>; + type Output = Result>, SendRequestError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); @@ -108,12 +108,13 @@ impl Future for SendClientRequest { res.into_client_response()._timeout(delay.take()).map_body( |head, payload| { if *response_decompress { - Payload::Stream(Decoder::from_headers(payload, &head.headers)) + Payload::Stream { + payload: Decoder::from_headers(payload, &head.headers), + } } else { - Payload::Stream(Decoder::new( - payload, - ContentEncoding::Identity, - )) + Payload::Stream { + payload: Decoder::new(payload, ContentEncoding::Identity), + } } }, ) diff --git a/awc/src/test.rs b/awc/src/test.rs index 1b41efc93..96ae1f0a1 100644 --- a/awc/src/test.rs +++ b/awc/src/test.rs @@ -65,7 +65,7 @@ impl TestResponse { /// Set response's payload pub fn set_payload>(mut self, data: B) -> Self { - let mut payload = h1::Payload::empty(); + let (_, mut payload) = h1::Payload::create(true); payload.unread_data(data.into()); self.payload = Some(payload.into()); self @@ -90,7 +90,8 @@ impl TestResponse { if let Some(pl) = self.payload { ClientResponse::new(head, pl) } else { - ClientResponse::new(head, h1::Payload::empty().into()) + let (_, payload) = h1::Payload::create(true); + ClientResponse::new(head, payload.into()) } } } diff --git a/src/dev.rs b/src/dev.rs index 23a40f292..6e1970467 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -14,7 +14,7 @@ pub use crate::types::form::UrlEncoded; pub use crate::types::json::JsonBody; pub use crate::types::readlines::Readlines; -pub use actix_http::{Extensions, Payload, PayloadStream, RequestHead, Response, ResponseHead}; +pub use actix_http::{Extensions, Payload, RequestHead, Response, ResponseHead}; pub use actix_router::{Path, ResourceDef, ResourcePath, Url}; pub use actix_server::{Server, ServerHandle}; pub use actix_service::{ diff --git a/src/response/builder.rs b/src/response/builder.rs index b500ab331..93d8ab567 100644 --- a/src/response/builder.rs +++ b/src/response/builder.rs @@ -429,9 +429,12 @@ mod tests { use actix_http::body; use super::*; - use crate::http::{ - header::{self, HeaderValue, CONTENT_TYPE}, - StatusCode, + use crate::{ + http::{ + header::{self, HeaderValue, CONTENT_TYPE}, + StatusCode, + }, + test::assert_body_eq, }; #[test] @@ -472,32 +475,23 @@ mod tests { #[actix_rt::test] async fn test_json() { - let resp = HttpResponse::Ok().json(vec!["v1", "v2", "v3"]); - let ct = resp.headers().get(CONTENT_TYPE).unwrap(); + let res = HttpResponse::Ok().json(vec!["v1", "v2", "v3"]); + let ct = res.headers().get(CONTENT_TYPE).unwrap(); assert_eq!(ct, HeaderValue::from_static("application/json")); - assert_eq!( - body::to_bytes(resp.into_body()).await.unwrap().as_ref(), - br#"["v1","v2","v3"]"# - ); + assert_body_eq!(res, br#"["v1","v2","v3"]"#); - let resp = HttpResponse::Ok().json(&["v1", "v2", "v3"]); - let ct = resp.headers().get(CONTENT_TYPE).unwrap(); + let res = HttpResponse::Ok().json(&["v1", "v2", "v3"]); + let ct = res.headers().get(CONTENT_TYPE).unwrap(); assert_eq!(ct, HeaderValue::from_static("application/json")); - assert_eq!( - body::to_bytes(resp.into_body()).await.unwrap().as_ref(), - br#"["v1","v2","v3"]"# - ); + assert_body_eq!(res, br#"["v1","v2","v3"]"#); // content type override - let resp = HttpResponse::Ok() + let res = HttpResponse::Ok() .insert_header((CONTENT_TYPE, "text/json")) .json(&vec!["v1", "v2", "v3"]); - let ct = resp.headers().get(CONTENT_TYPE).unwrap(); + let ct = res.headers().get(CONTENT_TYPE).unwrap(); assert_eq!(ct, HeaderValue::from_static("text/json")); - assert_eq!( - body::to_bytes(resp.into_body()).await.unwrap().as_ref(), - br#"["v1","v2","v3"]"# - ); + assert_body_eq!(res, br#"["v1","v2","v3"]"#); } #[actix_rt::test] diff --git a/src/service.rs b/src/service.rs index 9ccf5274d..d5c381fa4 100644 --- a/src/service.rs +++ b/src/service.rs @@ -7,7 +7,7 @@ use std::{ use actix_http::{ body::{BoxBody, EitherBody, MessageBody}, header::HeaderMap, - Extensions, HttpMessage, Method, Payload, PayloadStream, RequestHead, Response, + BoxedPayloadStream, Extensions, HttpMessage, Method, Payload, RequestHead, Response, ResponseHead, StatusCode, Uri, Version, }; use actix_router::{IntoPatterns, Path, Patterns, Resource, ResourceDef, Url}; @@ -293,7 +293,7 @@ impl Resource for ServiceRequest { } impl HttpMessage for ServiceRequest { - type Stream = PayloadStream; + type Stream = BoxedPayloadStream; #[inline] /// Returns Request's headers. diff --git a/src/test/test_request.rs b/src/test/test_request.rs index fd3355ef3..5c4de9084 100644 --- a/src/test/test_request.rs +++ b/src/test/test_request.rs @@ -174,25 +174,28 @@ impl TestRequest { } /// Set request payload. - pub fn set_payload>(mut self, data: B) -> Self { + pub fn set_payload(mut self, data: impl Into) -> Self { self.req.set_payload(data); self } - /// Serialize `data` to a URL encoded form and set it as the request payload. The `Content-Type` - /// header is set to `application/x-www-form-urlencoded`. - pub fn set_form(mut self, data: &T) -> Self { - let bytes = serde_urlencoded::to_string(data) + /// Serialize `data` to a URL encoded form and set it as the request payload. + /// + /// The `Content-Type` header is set to `application/x-www-form-urlencoded`. + pub fn set_form(mut self, data: impl Serialize) -> Self { + let bytes = serde_urlencoded::to_string(&data) .expect("Failed to serialize test data as a urlencoded form"); self.req.set_payload(bytes); self.req.insert_header(ContentType::form_url_encoded()); self } - /// Serialize `data` to JSON and set it as the request payload. The `Content-Type` header is - /// set to `application/json`. - pub fn set_json(mut self, data: &T) -> Self { - let bytes = serde_json::to_string(data).expect("Failed to serialize test data to json"); + /// Serialize `data` to JSON and set it as the request payload. + /// + /// The `Content-Type` header is set to `application/json`. + pub fn set_json(mut self, data: impl Serialize) -> Self { + let bytes = + serde_json::to_string(&data).expect("Failed to serialize test data to json"); self.req.set_payload(bytes); self.req.insert_header(ContentType::json()); self From d2590fd46cbab9cf96b3e6864430f675f4512835 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 25 Dec 2021 02:33:37 +0000 Subject: [PATCH 69/87] `ClientRequest::send_body` takes `impl MessageBody` (#2546) --- .github/workflows/ci-master.yml | 66 ++++ .github/workflows/ci.yml | 62 ---- awc/CHANGES.md | 6 + awc/src/any_body.rs | 19 +- awc/src/connect.rs | 2 +- awc/src/frozen.rs | 6 +- awc/src/lib.rs | 5 +- awc/src/middleware/redirect.rs | 4 +- awc/src/request.rs | 74 ++-- awc/src/response.rs | 556 ----------------------------- awc/src/responses/json_body.rs | 192 ++++++++++ awc/src/responses/mod.rs | 49 +++ awc/src/responses/read_body.rs | 61 ++++ awc/src/responses/response.rs | 257 +++++++++++++ awc/src/responses/response_body.rs | 144 ++++++++ awc/src/sender.rs | 22 +- awc/src/ws.rs | 3 +- src/guard.rs | 12 +- 18 files changed, 853 insertions(+), 687 deletions(-) create mode 100644 .github/workflows/ci-master.yml delete mode 100644 awc/src/response.rs create mode 100644 awc/src/responses/json_body.rs create mode 100644 awc/src/responses/mod.rs create mode 100644 awc/src/responses/read_body.rs create mode 100644 awc/src/responses/response.rs create mode 100644 awc/src/responses/response_body.rs diff --git a/.github/workflows/ci-master.yml b/.github/workflows/ci-master.yml new file mode 100644 index 000000000..548ec21b7 --- /dev/null +++ b/.github/workflows/ci-master.yml @@ -0,0 +1,66 @@ +name: CI (master only) + +on: + push: + branches: [master] + +jobs: + ci_feature_powerset_check: + name: Verify Feature Combinations + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable-x86_64-unknown-linux-gnu + profile: minimal + override: true + + - name: Generate Cargo.lock + uses: actions-rs/cargo@v1 + with: { command: generate-lockfile } + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1.2.0 + + - name: Install cargo-hack + uses: actions-rs/cargo@v1 + with: + command: install + args: cargo-hack + + - name: check feature combinations + uses: actions-rs/cargo@v1 + with: { command: ci-check-all-feature-powerset } + + - name: check feature combinations + uses: actions-rs/cargo@v1 + with: { command: ci-check-all-feature-powerset-linux } + + coverage: + name: coverage + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable-x86_64-unknown-linux-gnu + profile: minimal + override: true + + - name: Generate Cargo.lock + uses: actions-rs/cargo@v1 + with: { command: generate-lockfile } + - name: Cache Dependencies + uses: Swatinem/rust-cache@v1.2.0 + + - name: Generate coverage file + run: | + cargo install cargo-tarpaulin --vers "^0.13" + cargo tarpaulin --workspace --features=rustls,openssl --out Xml --verbose + - name: Upload to Codecov + uses: codecov/codecov-action@v1 + with: { file: cobertura.xml } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9b98a7b8..fe464bf27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,68 +96,6 @@ jobs: cargo install cargo-cache --version 0.6.3 --no-default-features --features ci-autoclean cargo-cache - ci_feature_powerset_check: - name: Verify Feature Combinations - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Install stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable-x86_64-unknown-linux-gnu - profile: minimal - override: true - - - name: Generate Cargo.lock - uses: actions-rs/cargo@v1 - with: { command: generate-lockfile } - - name: Cache Dependencies - uses: Swatinem/rust-cache@v1.2.0 - - - name: Install cargo-hack - uses: actions-rs/cargo@v1 - with: - command: install - args: cargo-hack - - - name: check feature combinations - uses: actions-rs/cargo@v1 - with: { command: ci-check-all-feature-powerset } - - - name: check feature combinations - uses: actions-rs/cargo@v1 - with: { command: ci-check-all-feature-powerset-linux } - - coverage: - name: coverage - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Install stable - uses: actions-rs/toolchain@v1 - with: - toolchain: stable-x86_64-unknown-linux-gnu - profile: minimal - override: true - - - name: Generate Cargo.lock - uses: actions-rs/cargo@v1 - with: { command: generate-lockfile } - - name: Cache Dependencies - uses: Swatinem/rust-cache@v1.2.0 - - - name: Generate coverage file - if: github.ref == 'refs/heads/master' - run: | - cargo install cargo-tarpaulin --vers "^0.13" - cargo tarpaulin --workspace --features=rustls,openssl --out Xml --verbose - - name: Upload to Codecov - if: github.ref == 'refs/heads/master' - uses: codecov/codecov-action@v1 - with: { file: cobertura.xml } - rustdoc: name: doc tests runs-on: ubuntu-latest diff --git a/awc/CHANGES.md b/awc/CHANGES.md index b5144b7a2..e1a059481 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -3,8 +3,14 @@ ## Unreleased - 2021-xx-xx - Rename `Connector::{ssl => openssl}`. [#2503] - Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503] +- `ClientRequest::send_body` now takes an `impl MessageBody`. [#2546] +- Rename `MessageBody => ResponseBody` to avoid conflicts with `MessageBody` trait. [#2546] +- `impl Future` for `ResponseBody` no longer requires the body type be `Unpin`. [#2546] +- `impl Future` for `JsonBody` no longer requires the body type be `Unpin`. [#2546] +- `impl Stream` for `ClientResponse` no longer requires the body type be `Unpin`. [#2546] [#2503]: https://github.com/actix/actix-web/pull/2503 +[#2546]: https://github.com/actix/actix-web/pull/2546 ## 3.0.0-beta.14 - 2021-12-17 diff --git a/awc/src/any_body.rs b/awc/src/any_body.rs index 2ffeb5074..437216313 100644 --- a/awc/src/any_body.rs +++ b/awc/src/any_body.rs @@ -77,10 +77,27 @@ impl AnyBody where B: MessageBody + 'static, { + /// Converts a [`MessageBody`] type into the best possible representation. + /// + /// Checks size for `None` and tries to convert to `Bytes`. Otherwise, uses the `Body` variant. + pub fn from_message_body(body: B) -> Self + where + B: MessageBody, + { + if matches!(body.size(), BodySize::None) { + return Self::None; + } + + match body.try_into_bytes() { + Ok(body) => Self::Bytes { body }, + Err(body) => Self::new(body), + } + } + pub fn into_boxed(self) -> AnyBody { match self { Self::None => AnyBody::None, - Self::Bytes { body: bytes } => AnyBody::Bytes { body: bytes }, + Self::Bytes { body } => AnyBody::Bytes { body }, Self::Body { body } => AnyBody::new_boxed(body), } } diff --git a/awc/src/connect.rs b/awc/src/connect.rs index 19870b069..f93014a67 100644 --- a/awc/src/connect.rs +++ b/awc/src/connect.rs @@ -16,7 +16,7 @@ use crate::{ client::{ Connect as ClientConnect, ConnectError, Connection, ConnectionIo, SendRequestError, }, - response::ClientResponse, + ClientResponse, }; pub type BoxConnectorService = Rc< diff --git a/awc/src/frozen.rs b/awc/src/frozen.rs index cd93a1d60..b98d8d5e1 100644 --- a/awc/src/frozen.rs +++ b/awc/src/frozen.rs @@ -5,13 +5,13 @@ use futures_core::Stream; use serde::Serialize; use actix_http::{ + body::MessageBody, error::HttpError, header::{HeaderMap, HeaderName, TryIntoHeaderValue}, Method, RequestHead, Uri, }; use crate::{ - any_body::AnyBody, sender::{RequestSender, SendClientRequest}, BoxError, ClientConfig, }; @@ -46,7 +46,7 @@ impl FrozenClientRequest { /// Send a body. pub fn send_body(&self, body: B) -> SendClientRequest where - B: Into, + B: MessageBody + 'static, { RequestSender::Rc(self.head.clone(), None).send_body( self.addr, @@ -159,7 +159,7 @@ impl FrozenSendBuilder { /// Complete request construction and send a body. pub fn send_body(self, body: B) -> SendClientRequest where - B: Into, + B: MessageBody + 'static, { if let Some(e) = self.err { return e.into(); diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 00c559406..cef8e03dc 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -113,7 +113,7 @@ pub mod error; mod frozen; pub mod middleware; mod request; -mod response; +mod responses; mod sender; pub mod test; pub mod ws; @@ -128,7 +128,8 @@ pub use self::client::Connector; pub use self::connect::{BoxConnectorService, BoxedSocket, ConnectRequest, ConnectResponse}; pub use self::frozen::{FrozenClientRequest, FrozenSendBuilder}; pub use self::request::ClientRequest; -pub use self::response::{ClientResponse, JsonBody, MessageBody}; +#[allow(deprecated)] +pub use self::responses::{ClientResponse, JsonBody, MessageBody, ResponseBody}; pub use self::sender::SendClientRequest; use std::{convert::TryFrom, rc::Rc, time::Duration}; diff --git a/awc/src/middleware/redirect.rs b/awc/src/middleware/redirect.rs index 704d2d79d..0ee969eee 100644 --- a/awc/src/middleware/redirect.rs +++ b/awc/src/middleware/redirect.rs @@ -190,9 +190,7 @@ where let body_new = if is_redirect { // try to reuse body match body { - Some(ref bytes) => AnyBody::Bytes { - body: bytes.clone(), - }, + Some(ref bytes) => AnyBody::from(bytes.clone()), // TODO: should this be AnyBody::Empty or AnyBody::None. _ => AnyBody::empty(), } diff --git a/awc/src/request.rs b/awc/src/request.rs index 9e37b2755..3eb76e3f6 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -5,13 +5,13 @@ use futures_core::Stream; use serde::Serialize; use actix_http::{ + body::MessageBody, error::HttpError, header::{self, HeaderMap, HeaderValue, TryIntoHeaderPair}, ConnectionType, Method, RequestHead, Uri, Version, }; use crate::{ - any_body::AnyBody, error::{FreezeRequestError, InvalidUrl}, frozen::FrozenClientRequest, sender::{PrepForSendingError, RequestSender, SendClientRequest}, @@ -26,20 +26,20 @@ use crate::cookie::{Cookie, CookieJar}; /// This type can be used to construct an instance of `ClientRequest` through a /// builder-like pattern. /// -/// ``` -/// #[actix_rt::main] -/// async fn main() { -/// let response = awc::Client::new() -/// .get("http://www.rust-lang.org") // <- Create request builder -/// .insert_header(("User-Agent", "Actix-web")) -/// .send() // <- Send HTTP request -/// .await; +/// ```no_run +/// # #[actix_rt::main] +/// # async fn main() { +/// let response = awc::Client::new() +/// .get("http://www.rust-lang.org") // <- Create request builder +/// .insert_header(("User-Agent", "Actix-web")) +/// .send() // <- Send HTTP request +/// .await; /// -/// response.and_then(|response| { // <- server HTTP response -/// println!("Response: {:?}", response); -/// Ok(()) -/// }); -/// } +/// response.and_then(|response| { // <- server HTTP response +/// println!("Response: {:?}", response); +/// Ok(()) +/// }); +/// # } /// ``` pub struct ClientRequest { pub(crate) head: RequestHead, @@ -174,17 +174,13 @@ impl ClientRequest { /// Append a header, keeping any that were set with an equivalent field name. /// - /// ``` - /// # #[actix_rt::main] - /// # async fn main() { - /// # use awc::Client; - /// use awc::http::header::CONTENT_TYPE; + /// ```no_run + /// use awc::{http::header, Client}; /// /// Client::new() /// .get("http://www.rust-lang.org") /// .insert_header(("X-TEST", "value")) - /// .insert_header((CONTENT_TYPE, mime::APPLICATION_JSON)); - /// # } + /// .insert_header((header::CONTENT_TYPE, mime::APPLICATION_JSON)); /// ``` pub fn append_header(mut self, header: impl TryIntoHeaderPair) -> Self { match header.try_into_pair() { @@ -252,23 +248,25 @@ impl ClientRequest { /// Set a cookie /// - /// ``` - /// #[actix_rt::main] - /// async fn main() { - /// let resp = awc::Client::new().get("https://www.rust-lang.org") - /// .cookie( - /// awc::cookie::Cookie::build("name", "value") - /// .domain("www.rust-lang.org") - /// .path("/") - /// .secure(true) - /// .http_only(true) - /// .finish(), - /// ) - /// .send() - /// .await; + /// ```no_run + /// use awc::{cookie, Client}; /// - /// println!("Response: {:?}", resp); - /// } + /// # #[actix_rt::main] + /// # async fn main() { + /// let resp = Client::new().get("https://www.rust-lang.org") + /// .cookie( + /// awc::cookie::Cookie::build("name", "value") + /// .domain("www.rust-lang.org") + /// .path("/") + /// .secure(true) + /// .http_only(true) + /// .finish(), + /// ) + /// .send() + /// .await; + /// + /// println!("Response: {:?}", resp); + /// # } /// ``` #[cfg(feature = "cookies")] pub fn cookie(mut self, cookie: Cookie<'_>) -> Self { @@ -340,7 +338,7 @@ impl ClientRequest { /// Complete request construction and send body. pub fn send_body(self, body: B) -> SendClientRequest where - B: Into, + B: MessageBody + 'static, { let slf = match self.prep_for_sending() { Ok(slf) => slf, diff --git a/awc/src/response.rs b/awc/src/response.rs deleted file mode 100644 index 78cc339b4..000000000 --- a/awc/src/response.rs +++ /dev/null @@ -1,556 +0,0 @@ -use std::{ - cell::{Ref, RefMut}, - fmt, - future::Future, - io, - marker::PhantomData, - pin::Pin, - task::{Context, Poll}, - time::{Duration, Instant}, -}; - -use actix_http::{ - error::PayloadError, header, header::HeaderMap, BoxedPayloadStream, Extensions, - HttpMessage, Payload, ResponseHead, StatusCode, Version, -}; -use actix_rt::time::{sleep, Sleep}; -use bytes::{Bytes, BytesMut}; -use futures_core::{ready, Stream}; -use serde::de::DeserializeOwned; - -#[cfg(feature = "cookies")] -use crate::cookie::{Cookie, ParseError as CookieParseError}; -use crate::error::JsonPayloadError; - -/// Client Response -pub struct ClientResponse { - pub(crate) head: ResponseHead, - pub(crate) payload: Payload, - pub(crate) timeout: ResponseTimeout, -} - -/// helper enum with reusable sleep passed from `SendClientResponse`. -/// See `ClientResponse::_timeout` for reason. -pub(crate) enum ResponseTimeout { - Disabled(Option>>), - Enabled(Pin>), -} - -impl Default for ResponseTimeout { - fn default() -> Self { - Self::Disabled(None) - } -} - -impl ResponseTimeout { - fn poll_timeout(&mut self, cx: &mut Context<'_>) -> Result<(), PayloadError> { - match *self { - Self::Enabled(ref mut timeout) => { - if timeout.as_mut().poll(cx).is_ready() { - Err(PayloadError::Io(io::Error::new( - io::ErrorKind::TimedOut, - "Response Payload IO timed out", - ))) - } else { - Ok(()) - } - } - Self::Disabled(_) => Ok(()), - } - } -} - -impl HttpMessage for ClientResponse { - type Stream = S; - - fn headers(&self) -> &HeaderMap { - &self.head.headers - } - - fn take_payload(&mut self) -> Payload { - std::mem::replace(&mut self.payload, Payload::None) - } - - fn extensions(&self) -> Ref<'_, Extensions> { - self.head.extensions() - } - - fn extensions_mut(&self) -> RefMut<'_, Extensions> { - self.head.extensions_mut() - } -} - -impl ClientResponse { - /// Create new Request instance - pub(crate) fn new(head: ResponseHead, payload: Payload) -> Self { - ClientResponse { - head, - payload, - timeout: ResponseTimeout::default(), - } - } - - #[inline] - pub(crate) fn head(&self) -> &ResponseHead { - &self.head - } - - /// Read the Request Version. - #[inline] - pub fn version(&self) -> Version { - self.head().version - } - - /// Get the status from the server. - #[inline] - pub fn status(&self) -> StatusCode { - self.head().status - } - - #[inline] - /// Returns request's headers. - pub fn headers(&self) -> &HeaderMap { - &self.head().headers - } - - /// Set a body and return previous body value - pub fn map_body(mut self, f: F) -> ClientResponse - where - F: FnOnce(&mut ResponseHead, Payload) -> Payload, - { - let payload = f(&mut self.head, self.payload); - - ClientResponse { - payload, - head: self.head, - timeout: self.timeout, - } - } - - /// Set a timeout duration for [`ClientResponse`](self::ClientResponse). - /// - /// This duration covers the duration of processing the response body stream - /// and would end it as timeout error when deadline met. - /// - /// Disabled by default. - pub fn timeout(self, dur: Duration) -> Self { - let timeout = match self.timeout { - ResponseTimeout::Disabled(Some(mut timeout)) - | ResponseTimeout::Enabled(mut timeout) => match Instant::now().checked_add(dur) { - Some(deadline) => { - timeout.as_mut().reset(deadline.into()); - ResponseTimeout::Enabled(timeout) - } - None => ResponseTimeout::Enabled(Box::pin(sleep(dur))), - }, - _ => ResponseTimeout::Enabled(Box::pin(sleep(dur))), - }; - - Self { - payload: self.payload, - head: self.head, - timeout, - } - } - - /// This method does not enable timeout. It's used to pass the boxed `Sleep` from - /// `SendClientRequest` and reuse it's heap allocation together with it's slot in - /// timer wheel. - pub(crate) fn _timeout(mut self, timeout: Option>>) -> Self { - self.timeout = ResponseTimeout::Disabled(timeout); - self - } - - /// Load request cookies. - #[cfg(feature = "cookies")] - pub fn cookies(&self) -> Result>>, CookieParseError> { - struct Cookies(Vec>); - - if self.extensions().get::().is_none() { - let mut cookies = Vec::new(); - for hdr in self.headers().get_all(&header::SET_COOKIE) { - let s = std::str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?; - cookies.push(Cookie::parse_encoded(s)?.into_owned()); - } - self.extensions_mut().insert(Cookies(cookies)); - } - Ok(Ref::map(self.extensions(), |ext| { - &ext.get::().unwrap().0 - })) - } - - /// Return request cookie. - #[cfg(feature = "cookies")] - pub fn cookie(&self, name: &str) -> Option> { - if let Ok(cookies) = self.cookies() { - for cookie in cookies.iter() { - if cookie.name() == name { - return Some(cookie.to_owned()); - } - } - } - None - } -} - -impl ClientResponse -where - S: Stream>, -{ - /// Loads HTTP response's body. - pub fn body(&mut self) -> MessageBody { - MessageBody::new(self) - } - - /// Loads and parse `application/json` encoded body. - /// Return `JsonBody` future. It resolves to a `T` value. - /// - /// Returns error: - /// - /// * content type is not `application/json` - /// * content length is greater than 256k - pub fn json(&mut self) -> JsonBody { - JsonBody::new(self) - } -} - -impl Stream for ClientResponse -where - S: Stream> + Unpin, -{ - type Item = Result; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let this = self.get_mut(); - this.timeout.poll_timeout(cx)?; - - Pin::new(&mut this.payload).poll_next(cx) - } -} - -impl fmt::Debug for ClientResponse { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "\nClientResponse {:?} {}", self.version(), self.status(),)?; - writeln!(f, " headers:")?; - for (key, val) in self.headers().iter() { - writeln!(f, " {:?}: {:?}", key, val)?; - } - Ok(()) - } -} - -const DEFAULT_BODY_LIMIT: usize = 2 * 1024 * 1024; - -/// Future that resolves to a complete HTTP message body. -pub struct MessageBody { - length: Option, - timeout: ResponseTimeout, - body: Result, Option>, -} - -impl MessageBody -where - S: Stream>, -{ - /// Create `MessageBody` for request. - pub fn new(res: &mut ClientResponse) -> MessageBody { - let length = match res.headers().get(&header::CONTENT_LENGTH) { - Some(value) => { - let len = value.to_str().ok().and_then(|s| s.parse::().ok()); - - match len { - None => return Self::err(PayloadError::UnknownLength), - len => len, - } - } - None => None, - }; - - MessageBody { - length, - timeout: std::mem::take(&mut res.timeout), - body: Ok(ReadBody::new(res.take_payload(), DEFAULT_BODY_LIMIT)), - } - } - - /// Change max size of payload. By default max size is 2048kB - pub fn limit(mut self, limit: usize) -> Self { - if let Ok(ref mut body) = self.body { - body.limit = limit; - } - self - } - - fn err(e: PayloadError) -> Self { - MessageBody { - length: None, - timeout: ResponseTimeout::default(), - body: Err(Some(e)), - } - } -} - -impl Future for MessageBody -where - S: Stream> + Unpin, -{ - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); - - match this.body { - Err(ref mut err) => Poll::Ready(Err(err.take().unwrap())), - Ok(ref mut body) => { - if let Some(len) = this.length.take() { - if len > body.limit { - return Poll::Ready(Err(PayloadError::Overflow)); - } - } - - this.timeout.poll_timeout(cx)?; - - Pin::new(body).poll(cx) - } - } - } -} - -/// Response's payload json parser, it resolves to a deserialized `T` value. -/// -/// Returns error: -/// -/// * content type is not `application/json` -/// * content length is greater than 64k -pub struct JsonBody { - length: Option, - err: Option, - timeout: ResponseTimeout, - fut: Option>, - _phantom: PhantomData, -} - -impl JsonBody -where - S: Stream>, - U: DeserializeOwned, -{ - /// Create `JsonBody` for request. - pub fn new(res: &mut ClientResponse) -> Self { - // check content-type - let json = if let Ok(Some(mime)) = res.mime_type() { - mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON) - } else { - false - }; - if !json { - return JsonBody { - length: None, - fut: None, - timeout: ResponseTimeout::default(), - err: Some(JsonPayloadError::ContentType), - _phantom: PhantomData, - }; - } - - let mut len = None; - - if let Some(l) = res.headers().get(&header::CONTENT_LENGTH) { - if let Ok(s) = l.to_str() { - if let Ok(l) = s.parse::() { - len = Some(l) - } - } - } - - JsonBody { - length: len, - err: None, - timeout: std::mem::take(&mut res.timeout), - fut: Some(ReadBody::new(res.take_payload(), 65536)), - _phantom: PhantomData, - } - } - - /// Change max size of payload. By default max size is 64kB - pub fn limit(mut self, limit: usize) -> Self { - if let Some(ref mut fut) = self.fut { - fut.limit = limit; - } - self - } -} - -impl Unpin for JsonBody -where - T: Stream> + Unpin, - U: DeserializeOwned, -{ -} - -impl Future for JsonBody -where - T: Stream> + Unpin, - U: DeserializeOwned, -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if let Some(err) = self.err.take() { - return Poll::Ready(Err(err)); - } - - if let Some(len) = self.length.take() { - if len > self.fut.as_ref().unwrap().limit { - return Poll::Ready(Err(JsonPayloadError::Payload(PayloadError::Overflow))); - } - } - - self.timeout - .poll_timeout(cx) - .map_err(JsonPayloadError::Payload)?; - - let body = ready!(Pin::new(&mut self.get_mut().fut.as_mut().unwrap()).poll(cx))?; - Poll::Ready(serde_json::from_slice::(&body).map_err(JsonPayloadError::from)) - } -} - -struct ReadBody { - stream: Payload, - buf: BytesMut, - limit: usize, -} - -impl ReadBody { - fn new(stream: Payload, limit: usize) -> Self { - Self { - stream, - buf: BytesMut::new(), - limit, - } - } -} - -impl Future for ReadBody -where - S: Stream> + Unpin, -{ - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.get_mut(); - - while let Some(chunk) = ready!(Pin::new(&mut this.stream).poll_next(cx)?) { - if (this.buf.len() + chunk.len()) > this.limit { - return Poll::Ready(Err(PayloadError::Overflow)); - } - this.buf.extend_from_slice(&chunk); - } - - Poll::Ready(Ok(this.buf.split().freeze())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use serde::{Deserialize, Serialize}; - - use crate::{http::header, test::TestResponse}; - - #[actix_rt::test] - async fn test_body() { - let mut req = TestResponse::with_header((header::CONTENT_LENGTH, "xxxx")).finish(); - match req.body().await.err().unwrap() { - PayloadError::UnknownLength => {} - _ => unreachable!("error"), - } - - let mut req = TestResponse::with_header((header::CONTENT_LENGTH, "10000000")).finish(); - match req.body().await.err().unwrap() { - PayloadError::Overflow => {} - _ => unreachable!("error"), - } - - let mut req = TestResponse::default() - .set_payload(Bytes::from_static(b"test")) - .finish(); - assert_eq!(req.body().await.ok().unwrap(), Bytes::from_static(b"test")); - - let mut req = TestResponse::default() - .set_payload(Bytes::from_static(b"11111111111111")) - .finish(); - match req.body().limit(5).await.err().unwrap() { - PayloadError::Overflow => {} - _ => unreachable!("error"), - } - } - - #[derive(Serialize, Deserialize, PartialEq, Debug)] - struct MyObject { - name: String, - } - - fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool { - match err { - JsonPayloadError::Payload(PayloadError::Overflow) => { - matches!(other, JsonPayloadError::Payload(PayloadError::Overflow)) - } - JsonPayloadError::ContentType => matches!(other, JsonPayloadError::ContentType), - _ => false, - } - } - - #[actix_rt::test] - async fn test_json_body() { - let mut req = TestResponse::default().finish(); - let json = JsonBody::<_, MyObject>::new(&mut req).await; - assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); - - let mut req = TestResponse::default() - .insert_header(( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/text"), - )) - .finish(); - let json = JsonBody::<_, MyObject>::new(&mut req).await; - assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); - - let mut req = TestResponse::default() - .insert_header(( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - )) - .insert_header(( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("10000"), - )) - .finish(); - - let json = JsonBody::<_, MyObject>::new(&mut req).limit(100).await; - assert!(json_eq( - json.err().unwrap(), - JsonPayloadError::Payload(PayloadError::Overflow) - )); - - let mut req = TestResponse::default() - .insert_header(( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - )) - .insert_header(( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - )) - .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) - .finish(); - - let json = JsonBody::<_, MyObject>::new(&mut req).await; - assert_eq!( - json.ok().unwrap(), - MyObject { - name: "test".to_owned() - } - ); - } -} diff --git a/awc/src/responses/json_body.rs b/awc/src/responses/json_body.rs new file mode 100644 index 000000000..3912324b6 --- /dev/null +++ b/awc/src/responses/json_body.rs @@ -0,0 +1,192 @@ +use std::{ + future::Future, + marker::PhantomData, + mem, + pin::Pin, + task::{Context, Poll}, +}; + +use actix_http::{error::PayloadError, header, HttpMessage}; +use bytes::Bytes; +use futures_core::{ready, Stream}; +use pin_project_lite::pin_project; +use serde::de::DeserializeOwned; + +use super::{read_body::ReadBody, ResponseTimeout, DEFAULT_BODY_LIMIT}; +use crate::{error::JsonPayloadError, ClientResponse}; + +pin_project! { + /// A `Future` that reads a body stream, parses JSON, resolving to a deserialized `T`. + /// + /// # Errors + /// `Future` implementation returns error if: + /// - content type is not `application/json`; + /// - content length is greater than [limit](JsonBody::limit) (default: 2 MiB). + pub struct JsonBody { + #[pin] + body: Option>, + length: Option, + timeout: ResponseTimeout, + err: Option, + _phantom: PhantomData, + } +} + +impl JsonBody +where + S: Stream>, + T: DeserializeOwned, +{ + /// Creates a JSON body stream reader from a response by taking its payload. + pub fn new(res: &mut ClientResponse) -> Self { + // check content-type + let json = if let Ok(Some(mime)) = res.mime_type() { + mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON) + } else { + false + }; + + if !json { + return JsonBody { + length: None, + body: None, + timeout: ResponseTimeout::default(), + err: Some(JsonPayloadError::ContentType), + _phantom: PhantomData, + }; + } + + let length = res + .headers() + .get(&header::CONTENT_LENGTH) + .and_then(|len_hdr| len_hdr.to_str().ok()) + .and_then(|len_str| len_str.parse::().ok()); + + JsonBody { + body: Some(ReadBody::new(res.take_payload(), DEFAULT_BODY_LIMIT)), + length, + timeout: mem::take(&mut res.timeout), + err: None, + _phantom: PhantomData, + } + } + + /// Change max size of payload. Default limit is 2 MiB. + pub fn limit(mut self, limit: usize) -> Self { + if let Some(ref mut fut) = self.body { + fut.limit = limit; + } + + self + } +} + +impl Future for JsonBody +where + S: Stream>, + T: DeserializeOwned, +{ + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + if let Some(err) = this.err.take() { + return Poll::Ready(Err(err)); + } + + if let Some(len) = this.length.take() { + let body = Option::as_ref(&this.body).unwrap(); + if len > body.limit { + return Poll::Ready(Err(JsonPayloadError::Payload(PayloadError::Overflow))); + } + } + + this.timeout + .poll_timeout(cx) + .map_err(JsonPayloadError::Payload)?; + + let body = ready!(this.body.as_pin_mut().unwrap().poll(cx))?; + Poll::Ready(serde_json::from_slice::(&body).map_err(JsonPayloadError::from)) + } +} + +#[cfg(test)] +mod tests { + use actix_http::BoxedPayloadStream; + use serde::{Deserialize, Serialize}; + use static_assertions::assert_impl_all; + + use super::*; + use crate::{http::header, test::TestResponse}; + + assert_impl_all!(JsonBody: Unpin); + + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct MyObject { + name: String, + } + + fn json_eq(err: JsonPayloadError, other: JsonPayloadError) -> bool { + match err { + JsonPayloadError::Payload(PayloadError::Overflow) => { + matches!(other, JsonPayloadError::Payload(PayloadError::Overflow)) + } + JsonPayloadError::ContentType => matches!(other, JsonPayloadError::ContentType), + _ => false, + } + } + + #[actix_rt::test] + async fn read_json_body() { + let mut req = TestResponse::default().finish(); + let json = JsonBody::<_, MyObject>::new(&mut req).await; + assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); + + let mut req = TestResponse::default() + .insert_header(( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/text"), + )) + .finish(); + let json = JsonBody::<_, MyObject>::new(&mut req).await; + assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); + + let mut req = TestResponse::default() + .insert_header(( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + )) + .insert_header(( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("10000"), + )) + .finish(); + + let json = JsonBody::<_, MyObject>::new(&mut req).limit(100).await; + assert!(json_eq( + json.err().unwrap(), + JsonPayloadError::Payload(PayloadError::Overflow) + )); + + let mut req = TestResponse::default() + .insert_header(( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + )) + .insert_header(( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + )) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .finish(); + + let json = JsonBody::<_, MyObject>::new(&mut req).await; + assert_eq!( + json.ok().unwrap(), + MyObject { + name: "test".to_owned() + } + ); + } +} diff --git a/awc/src/responses/mod.rs b/awc/src/responses/mod.rs new file mode 100644 index 000000000..588ce014c --- /dev/null +++ b/awc/src/responses/mod.rs @@ -0,0 +1,49 @@ +use std::{future::Future, io, pin::Pin, task::Context}; + +use actix_http::error::PayloadError; +use actix_rt::time::Sleep; + +mod json_body; +mod read_body; +mod response; +mod response_body; + +pub use self::json_body::JsonBody; +pub use self::response::ClientResponse; +#[allow(deprecated)] +pub use self::response_body::{MessageBody, ResponseBody}; + +/// Default body size limit: 2 MiB +const DEFAULT_BODY_LIMIT: usize = 2 * 1024 * 1024; + +/// Helper enum with reusable sleep passed from `SendClientResponse`. +/// +/// See [`ClientResponse::_timeout`] for reason. +pub(crate) enum ResponseTimeout { + Disabled(Option>>), + Enabled(Pin>), +} + +impl Default for ResponseTimeout { + fn default() -> Self { + Self::Disabled(None) + } +} + +impl ResponseTimeout { + fn poll_timeout(&mut self, cx: &mut Context<'_>) -> Result<(), PayloadError> { + match *self { + Self::Enabled(ref mut timeout) => { + if timeout.as_mut().poll(cx).is_ready() { + Err(PayloadError::Io(io::Error::new( + io::ErrorKind::TimedOut, + "Response Payload IO timed out", + ))) + } else { + Ok(()) + } + } + Self::Disabled(_) => Ok(()), + } + } +} diff --git a/awc/src/responses/read_body.rs b/awc/src/responses/read_body.rs new file mode 100644 index 000000000..a32bbb984 --- /dev/null +++ b/awc/src/responses/read_body.rs @@ -0,0 +1,61 @@ +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + +use actix_http::{error::PayloadError, Payload}; +use bytes::{Bytes, BytesMut}; +use futures_core::{ready, Stream}; +use pin_project_lite::pin_project; + +pin_project! { + pub(crate) struct ReadBody { + #[pin] + pub(crate) stream: Payload, + pub(crate) buf: BytesMut, + pub(crate) limit: usize, + } +} + +impl ReadBody { + pub(crate) fn new(stream: Payload, limit: usize) -> Self { + Self { + stream, + buf: BytesMut::new(), + limit, + } + } +} + +impl Future for ReadBody +where + S: Stream>, +{ + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + while let Some(chunk) = ready!(this.stream.as_mut().poll_next(cx)?) { + if (this.buf.len() + chunk.len()) > *this.limit { + return Poll::Ready(Err(PayloadError::Overflow)); + } + + this.buf.extend_from_slice(&chunk); + } + + Poll::Ready(Ok(this.buf.split().freeze())) + } +} + +#[cfg(test)] +mod tests { + use static_assertions::assert_impl_all; + + use super::*; + use crate::any_body::AnyBody; + + assert_impl_all!(ReadBody<()>: Unpin); + assert_impl_all!(ReadBody: Unpin); +} diff --git a/awc/src/responses/response.rs b/awc/src/responses/response.rs new file mode 100644 index 000000000..6385aea19 --- /dev/null +++ b/awc/src/responses/response.rs @@ -0,0 +1,257 @@ +use std::{ + cell::{Ref, RefMut}, + fmt, mem, + pin::Pin, + task::{Context, Poll}, + time::{Duration, Instant}, +}; + +use actix_http::{ + error::PayloadError, header, header::HeaderMap, BoxedPayloadStream, Extensions, + HttpMessage, Payload, ResponseHead, StatusCode, Version, +}; +use actix_rt::time::{sleep, Sleep}; +use bytes::Bytes; +use futures_core::Stream; +use pin_project_lite::pin_project; +use serde::de::DeserializeOwned; + +#[cfg(feature = "cookies")] +use crate::cookie::{Cookie, ParseError as CookieParseError}; + +use super::{JsonBody, ResponseBody, ResponseTimeout}; + +pin_project! { + /// Client Response + pub struct ClientResponse { + pub(crate) head: ResponseHead, + #[pin] + pub(crate) payload: Payload, + pub(crate) timeout: ResponseTimeout, + } +} + +impl ClientResponse { + /// Create new Request instance + pub(crate) fn new(head: ResponseHead, payload: Payload) -> Self { + ClientResponse { + head, + payload, + timeout: ResponseTimeout::default(), + } + } + + #[inline] + pub(crate) fn head(&self) -> &ResponseHead { + &self.head + } + + /// Read the Request Version. + #[inline] + pub fn version(&self) -> Version { + self.head().version + } + + /// Get the status from the server. + #[inline] + pub fn status(&self) -> StatusCode { + self.head().status + } + + #[inline] + /// Returns request's headers. + pub fn headers(&self) -> &HeaderMap { + &self.head().headers + } + + /// Set a body and return previous body value + pub fn map_body(mut self, f: F) -> ClientResponse + where + F: FnOnce(&mut ResponseHead, Payload) -> Payload, + { + let payload = f(&mut self.head, self.payload); + + ClientResponse { + payload, + head: self.head, + timeout: self.timeout, + } + } + + /// Set a timeout duration for [`ClientResponse`](self::ClientResponse). + /// + /// This duration covers the duration of processing the response body stream + /// and would end it as timeout error when deadline met. + /// + /// Disabled by default. + pub fn timeout(self, dur: Duration) -> Self { + let timeout = match self.timeout { + ResponseTimeout::Disabled(Some(mut timeout)) + | ResponseTimeout::Enabled(mut timeout) => match Instant::now().checked_add(dur) { + Some(deadline) => { + timeout.as_mut().reset(deadline.into()); + ResponseTimeout::Enabled(timeout) + } + None => ResponseTimeout::Enabled(Box::pin(sleep(dur))), + }, + _ => ResponseTimeout::Enabled(Box::pin(sleep(dur))), + }; + + Self { + payload: self.payload, + head: self.head, + timeout, + } + } + + /// This method does not enable timeout. It's used to pass the boxed `Sleep` from + /// `SendClientRequest` and reuse it's heap allocation together with it's slot in + /// timer wheel. + pub(crate) fn _timeout(mut self, timeout: Option>>) -> Self { + self.timeout = ResponseTimeout::Disabled(timeout); + self + } + + /// Load request cookies. + #[cfg(feature = "cookies")] + pub fn cookies(&self) -> Result>>, CookieParseError> { + struct Cookies(Vec>); + + if self.extensions().get::().is_none() { + let mut cookies = Vec::new(); + for hdr in self.headers().get_all(&header::SET_COOKIE) { + let s = std::str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?; + cookies.push(Cookie::parse_encoded(s)?.into_owned()); + } + self.extensions_mut().insert(Cookies(cookies)); + } + Ok(Ref::map(self.extensions(), |ext| { + &ext.get::().unwrap().0 + })) + } + + /// Return request cookie. + #[cfg(feature = "cookies")] + pub fn cookie(&self, name: &str) -> Option> { + if let Ok(cookies) = self.cookies() { + for cookie in cookies.iter() { + if cookie.name() == name { + return Some(cookie.to_owned()); + } + } + } + None + } +} + +impl ClientResponse +where + S: Stream>, +{ + /// Returns a [`Future`] that consumes the body stream and resolves to [`Bytes`]. + /// + /// # Errors + /// `Future` implementation returns error if: + /// - content type is not `application/json` + /// - content length is greater than [limit](JsonBody::limit) (default: 2 MiB) + /// + /// # Examples + /// ```no_run + /// # use awc::Client; + /// # use bytes::Bytes; + /// # #[actix_rt::main] + /// # async fn async_ctx() -> Result<(), Box> { + /// let client = Client::default(); + /// let mut res = client.get("https://httpbin.org/robots.txt").send().await?; + /// let body: Bytes = res.body().await?; + /// # Ok(()) + /// # } + /// ``` + /// + /// [`Future`]: std::future::Future + pub fn body(&mut self) -> ResponseBody { + ResponseBody::new(self) + } + + /// Returns a [`Future`] consumes the body stream, parses JSON, and resolves to a deserialized + /// `T` value. + /// + /// # Errors + /// Future returns error if: + /// - content type is not `application/json`; + /// - content length is greater than [limit](JsonBody::limit) (default: 2 MiB). + /// + /// # Examples + /// ```no_run + /// # use awc::Client; + /// # #[actix_rt::main] + /// # async fn async_ctx() -> Result<(), Box> { + /// let client = Client::default(); + /// let mut res = client.get("https://httpbin.org/json").send().await?; + /// let val = res.json::().await?; + /// assert!(val.is_object()); + /// # Ok(()) + /// # } + /// ``` + /// + /// [`Future`]: std::future::Future + pub fn json(&mut self) -> JsonBody { + JsonBody::new(self) + } +} + +impl fmt::Debug for ClientResponse { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!(f, "\nClientResponse {:?} {}", self.version(), self.status(),)?; + writeln!(f, " headers:")?; + for (key, val) in self.headers().iter() { + writeln!(f, " {:?}: {:?}", key, val)?; + } + Ok(()) + } +} + +impl HttpMessage for ClientResponse { + type Stream = S; + + fn headers(&self) -> &HeaderMap { + &self.head.headers + } + + fn take_payload(&mut self) -> Payload { + mem::replace(&mut self.payload, Payload::None) + } + + fn extensions(&self) -> Ref<'_, Extensions> { + self.head.extensions() + } + + fn extensions_mut(&self) -> RefMut<'_, Extensions> { + self.head.extensions_mut() + } +} + +impl Stream for ClientResponse +where + S: Stream> + Unpin, +{ + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.project(); + this.timeout.poll_timeout(cx)?; + this.payload.poll_next(cx) + } +} + +#[cfg(test)] +mod tests { + use static_assertions::assert_impl_all; + + use super::*; + use crate::any_body::AnyBody; + + assert_impl_all!(ClientResponse: Unpin); + assert_impl_all!(ClientResponse<()>: Unpin); + assert_impl_all!(ClientResponse: Unpin); +} diff --git a/awc/src/responses/response_body.rs b/awc/src/responses/response_body.rs new file mode 100644 index 000000000..8d9d1274a --- /dev/null +++ b/awc/src/responses/response_body.rs @@ -0,0 +1,144 @@ +use std::{ + future::Future, + mem, + pin::Pin, + task::{Context, Poll}, +}; + +use actix_http::{error::PayloadError, header, HttpMessage}; +use bytes::Bytes; +use futures_core::Stream; +use pin_project_lite::pin_project; + +use super::{read_body::ReadBody, ResponseTimeout, DEFAULT_BODY_LIMIT}; +use crate::ClientResponse; + +pin_project! { + /// A `Future` that reads a body stream, resolving as [`Bytes`]. + /// + /// # Errors + /// `Future` implementation returns error if: + /// - content type is not `application/json`; + /// - content length is greater than [limit](JsonBody::limit) (default: 2 MiB). + pub struct ResponseBody { + #[pin] + body: Option>, + length: Option, + timeout: ResponseTimeout, + err: Option, + } +} + +#[deprecated(since = "3.0.0", note = "Renamed to `ResponseBody`.")] +pub type MessageBody = ResponseBody; + +impl ResponseBody +where + S: Stream>, +{ + /// Creates a body stream reader from a response by taking its payload. + pub fn new(res: &mut ClientResponse) -> ResponseBody { + let length = match res.headers().get(&header::CONTENT_LENGTH) { + Some(value) => { + let len = value.to_str().ok().and_then(|s| s.parse::().ok()); + + match len { + None => return Self::err(PayloadError::UnknownLength), + len => len, + } + } + None => None, + }; + + ResponseBody { + body: Some(ReadBody::new(res.take_payload(), DEFAULT_BODY_LIMIT)), + length, + timeout: mem::take(&mut res.timeout), + err: None, + } + } + + /// Change max size limit of payload. + /// + /// The default limit is 2 MiB. + pub fn limit(mut self, limit: usize) -> Self { + if let Some(ref mut body) = self.body { + body.limit = limit; + } + + self + } + + fn err(err: PayloadError) -> Self { + ResponseBody { + body: None, + length: None, + timeout: ResponseTimeout::default(), + err: Some(err), + } + } +} + +impl Future for ResponseBody +where + S: Stream>, +{ + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + if let Some(err) = this.err.take() { + return Poll::Ready(Err(err)); + } + + if let Some(len) = this.length.take() { + let body = Option::as_ref(&this.body).unwrap(); + if len > body.limit { + return Poll::Ready(Err(PayloadError::Overflow)); + } + } + + this.timeout.poll_timeout(cx)?; + + this.body.as_pin_mut().unwrap().poll(cx) + } +} + +#[cfg(test)] +mod tests { + use static_assertions::assert_impl_all; + + use super::*; + use crate::{http::header, test::TestResponse}; + + assert_impl_all!(ResponseBody<()>: Unpin); + + #[actix_rt::test] + async fn read_body() { + let mut req = TestResponse::with_header((header::CONTENT_LENGTH, "xxxx")).finish(); + match req.body().await.err().unwrap() { + PayloadError::UnknownLength => {} + _ => unreachable!("error"), + } + + let mut req = TestResponse::with_header((header::CONTENT_LENGTH, "10000000")).finish(); + match req.body().await.err().unwrap() { + PayloadError::Overflow => {} + _ => unreachable!("error"), + } + + let mut req = TestResponse::default() + .set_payload(Bytes::from_static(b"test")) + .finish(); + assert_eq!(req.body().await.ok().unwrap(), Bytes::from_static(b"test")); + + let mut req = TestResponse::default() + .set_payload(Bytes::from_static(b"11111111111111")) + .finish(); + match req.body().limit(5).await.err().unwrap() { + PayloadError::Overflow => {} + _ => unreachable!("error"), + } + } +} diff --git a/awc/src/sender.rs b/awc/src/sender.rs index 29c814531..71d705d38 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -8,7 +8,7 @@ use std::{ }; use actix_http::{ - body::BodyStream, + body::{BodyStream, MessageBody}, error::HttpError, header::{self, HeaderMap, HeaderName, TryIntoHeaderValue}, RequestHead, RequestHeadType, @@ -189,15 +189,17 @@ impl RequestSender { body: B, ) -> SendClientRequest where - B: Into, + B: MessageBody + 'static, { let req = match self { - RequestSender::Owned(head) => { - ConnectRequest::Client(RequestHeadType::Owned(head), body.into(), addr) - } + RequestSender::Owned(head) => ConnectRequest::Client( + RequestHeadType::Owned(head), + AnyBody::from_message_body(body).into_boxed(), + addr, + ), RequestSender::Rc(head, extra_headers) => ConnectRequest::Client( RequestHeadType::Rc(head, extra_headers), - body.into(), + AnyBody::from_message_body(body).into_boxed(), addr, ), }; @@ -229,9 +231,7 @@ impl RequestSender { response_decompress, timeout, config, - AnyBody::Bytes { - body: Bytes::from(body), - }, + AnyBody::from_message_body(body.into_bytes()), ) } @@ -260,9 +260,7 @@ impl RequestSender { response_decompress, timeout, config, - AnyBody::Bytes { - body: Bytes::from(body), - }, + AnyBody::from_message_body(body.into_bytes()), ) } diff --git a/awc/src/ws.rs b/awc/src/ws.rs index 06d54aadb..c63e22969 100644 --- a/awc/src/ws.rs +++ b/awc/src/ws.rs @@ -42,8 +42,7 @@ use crate::{ header::{self, HeaderName, HeaderValue, TryIntoHeaderValue, AUTHORIZATION}, ConnectionType, Method, StatusCode, Uri, Version, }, - response::ClientResponse, - ClientConfig, + ClientConfig, ClientResponse, }; #[cfg(feature = "cookies")] diff --git a/src/guard.rs b/src/guard.rs index a5770df89..d5c585c1b 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -270,13 +270,11 @@ impl Guard for HeaderGuard { /// ``` /// use actix_web::{web, guard::Host, App, HttpResponse}; /// -/// fn main() { -/// App::new().service( -/// web::resource("/index.html") -/// .guard(Host("www.rust-lang.org")) -/// .to(|| HttpResponse::MethodNotAllowed()) -/// ); -/// } +/// App::new().service( +/// web::resource("/index.html") +/// .guard(Host("www.rust-lang.org")) +/// .to(|| HttpResponse::MethodNotAllowed()) +/// ); /// ``` pub fn Host>(host: H) -> HostGuard { HostGuard(host.as_ref().to_string(), None) From 3756dfc2cea2049c393e2944cffeb84075a982e4 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 25 Dec 2021 02:23:22 +0000 Subject: [PATCH 70/87] move client to own module --- awc/src/builder.rs | 6 +- awc/src/client/mod.rs | 188 ++++++++++++++++++++++++++++++++++++++++-- awc/src/frozen.rs | 3 +- awc/src/lib.rs | 183 +--------------------------------------- awc/src/request.rs | 22 ++--- awc/src/sender.rs | 3 +- awc/src/ws.rs | 5 +- 7 files changed, 203 insertions(+), 207 deletions(-) diff --git a/awc/src/builder.rs b/awc/src/builder.rs index 30f203bb8..16a4e9cb5 100644 --- a/awc/src/builder.rs +++ b/awc/src/builder.rs @@ -9,11 +9,13 @@ use actix_rt::net::{ActixStream, TcpStream}; use actix_service::{boxed, Service}; use crate::{ - client::{ConnectInfo, Connector, ConnectorService, TcpConnectError, TcpConnection}, + client::{ + ClientConfig, ConnectInfo, Connector, ConnectorService, TcpConnectError, TcpConnection, + }, connect::DefaultConnector, error::SendRequestError, middleware::{NestTransform, Redirect, Transform}, - Client, ClientConfig, ConnectRequest, ConnectResponse, + Client, ConnectRequest, ConnectResponse, }; /// An HTTP Client builder diff --git a/awc/src/client/mod.rs b/awc/src/client/mod.rs index 0d5c899bc..d5854d83e 100644 --- a/awc/src/client/mod.rs +++ b/awc/src/client/mod.rs @@ -1,6 +1,15 @@ //! HTTP client. -use http::Uri; +use std::{convert::TryFrom, rc::Rc, time::Duration}; + +use actix_http::{error::HttpError, header::HeaderMap, Method, RequestHead, Uri}; +use actix_rt::net::TcpStream; +use actix_service::Service; +pub use actix_tls::connect::{ + ConnectError as TcpConnectError, ConnectInfo, Connection as TcpConnection, +}; + +use crate::{ws, BoxConnectorService, ClientBuilder, ClientRequest}; mod config; mod connection; @@ -10,10 +19,6 @@ mod h1proto; mod h2proto; mod pool; -pub use actix_tls::connect::{ - ConnectError as TcpConnectError, ConnectInfo, Connection as TcpConnection, -}; - pub use self::connection::{Connection, ConnectionIo}; pub use self::connector::{Connector, ConnectorService}; pub use self::error::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError}; @@ -23,3 +28,176 @@ pub struct Connect { pub uri: Uri, pub addr: Option, } + +/// An asynchronous HTTP and WebSocket client. +/// +/// You should take care to create, at most, one `Client` per thread. Otherwise, expect higher CPU +/// and memory usage. +/// +/// # Examples +/// ``` +/// use awc::Client; +/// +/// #[actix_rt::main] +/// async fn main() { +/// let mut client = Client::default(); +/// +/// let res = client.get("http://www.rust-lang.org") +/// .insert_header(("User-Agent", "my-app/1.2")) +/// .send() +/// .await; +/// +/// println!("Response: {:?}", res); +/// } +/// ``` +#[derive(Clone)] +pub struct Client(pub(crate) ClientConfig); + +#[derive(Clone)] +pub(crate) struct ClientConfig { + pub(crate) connector: BoxConnectorService, + pub(crate) default_headers: Rc, + pub(crate) timeout: Option, +} + +impl Default for Client { + fn default() -> Self { + ClientBuilder::new().finish() + } +} + +impl Client { + /// Create new client instance with default settings. + pub fn new() -> Client { + Client::default() + } + + /// Create `Client` builder. + /// This function is equivalent of `ClientBuilder::new()`. + pub fn builder() -> ClientBuilder< + impl Service< + ConnectInfo, + Response = TcpConnection, + Error = TcpConnectError, + > + Clone, + > { + ClientBuilder::new() + } + + /// Construct HTTP request. + pub fn request(&self, method: Method, url: U) -> ClientRequest + where + Uri: TryFrom, + >::Error: Into, + { + let mut req = ClientRequest::new(method, url, self.0.clone()); + + for header in self.0.default_headers.iter() { + // header map is empty + // TODO: probably append instead + req = req.insert_header_if_none(header); + } + req + } + + /// Create `ClientRequest` from `RequestHead` + /// + /// It is useful for proxy requests. This implementation + /// copies all headers and the method. + pub fn request_from(&self, url: U, head: &RequestHead) -> ClientRequest + where + Uri: TryFrom, + >::Error: Into, + { + let mut req = self.request(head.method.clone(), url); + for header in head.headers.iter() { + req = req.insert_header_if_none(header); + } + req + } + + /// Construct HTTP *GET* request. + pub fn get(&self, url: U) -> ClientRequest + where + Uri: TryFrom, + >::Error: Into, + { + self.request(Method::GET, url) + } + + /// Construct HTTP *HEAD* request. + pub fn head(&self, url: U) -> ClientRequest + where + Uri: TryFrom, + >::Error: Into, + { + self.request(Method::HEAD, url) + } + + /// Construct HTTP *PUT* request. + pub fn put(&self, url: U) -> ClientRequest + where + Uri: TryFrom, + >::Error: Into, + { + self.request(Method::PUT, url) + } + + /// Construct HTTP *POST* request. + pub fn post(&self, url: U) -> ClientRequest + where + Uri: TryFrom, + >::Error: Into, + { + self.request(Method::POST, url) + } + + /// Construct HTTP *PATCH* request. + pub fn patch(&self, url: U) -> ClientRequest + where + Uri: TryFrom, + >::Error: Into, + { + self.request(Method::PATCH, url) + } + + /// Construct HTTP *DELETE* request. + pub fn delete(&self, url: U) -> ClientRequest + where + Uri: TryFrom, + >::Error: Into, + { + self.request(Method::DELETE, url) + } + + /// Construct HTTP *OPTIONS* request. + pub fn options(&self, url: U) -> ClientRequest + where + Uri: TryFrom, + >::Error: Into, + { + self.request(Method::OPTIONS, url) + } + + /// Initialize a WebSocket connection. + /// Returns a WebSocket connection builder. + pub fn ws(&self, url: U) -> ws::WebsocketsRequest + where + Uri: TryFrom, + >::Error: Into, + { + let mut req = ws::WebsocketsRequest::new(url, self.0.clone()); + for (key, value) in self.0.default_headers.iter() { + req.head.headers.insert(key.clone(), value.clone()); + } + req + } + + /// Get default HeaderMap of Client. + /// + /// Returns Some(&mut HeaderMap) when Client object is unique + /// (No other clone of client exists at the same time). + pub fn headers(&mut self) -> Option<&mut HeaderMap> { + Rc::get_mut(&mut self.0.default_headers) + } +} diff --git a/awc/src/frozen.rs b/awc/src/frozen.rs index b98d8d5e1..14ecf9f32 100644 --- a/awc/src/frozen.rs +++ b/awc/src/frozen.rs @@ -12,8 +12,9 @@ use actix_http::{ }; use crate::{ + client::ClientConfig, sender::{RequestSender, SendClientRequest}, - BoxError, ClientConfig, + BoxError, }; /// `FrozenClientRequest` struct represents cloneable client request. diff --git a/awc/src/lib.rs b/awc/src/lib.rs index cef8e03dc..348d9312b 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -124,7 +124,7 @@ pub use actix_http as http; pub use cookie; pub use self::builder::ClientBuilder; -pub use self::client::Connector; +pub use self::client::{Client, Connector}; pub use self::connect::{BoxConnectorService, BoxedSocket, ConnectRequest, ConnectResponse}; pub use self::frozen::{FrozenClientRequest, FrozenSendBuilder}; pub use self::request::ClientRequest; @@ -132,185 +132,4 @@ pub use self::request::ClientRequest; pub use self::responses::{ClientResponse, JsonBody, MessageBody, ResponseBody}; pub use self::sender::SendClientRequest; -use std::{convert::TryFrom, rc::Rc, time::Duration}; - -use actix_http::{error::HttpError, header::HeaderMap, Method, RequestHead, Uri}; -use actix_rt::net::TcpStream; -use actix_service::Service; - -use self::client::{ConnectInfo, TcpConnectError, TcpConnection}; - pub(crate) type BoxError = Box; - -/// An asynchronous HTTP and WebSocket client. -/// -/// You should take care to create, at most, one `Client` per thread. Otherwise, expect higher CPU -/// and memory usage. -/// -/// # Examples -/// ``` -/// use awc::Client; -/// -/// #[actix_rt::main] -/// async fn main() { -/// let mut client = Client::default(); -/// -/// let res = client.get("http://www.rust-lang.org") -/// .insert_header(("User-Agent", "my-app/1.2")) -/// .send() -/// .await; -/// -/// println!("Response: {:?}", res); -/// } -/// ``` -#[derive(Clone)] -pub struct Client(ClientConfig); - -#[derive(Clone)] -pub(crate) struct ClientConfig { - pub(crate) connector: BoxConnectorService, - pub(crate) default_headers: Rc, - pub(crate) timeout: Option, -} - -impl Default for Client { - fn default() -> Self { - ClientBuilder::new().finish() - } -} - -impl Client { - /// Create new client instance with default settings. - pub fn new() -> Client { - Client::default() - } - - /// Create `Client` builder. - /// This function is equivalent of `ClientBuilder::new()`. - pub fn builder() -> ClientBuilder< - impl Service< - ConnectInfo, - Response = TcpConnection, - Error = TcpConnectError, - > + Clone, - > { - ClientBuilder::new() - } - - /// Construct HTTP request. - pub fn request(&self, method: Method, url: U) -> ClientRequest - where - Uri: TryFrom, - >::Error: Into, - { - let mut req = ClientRequest::new(method, url, self.0.clone()); - - for header in self.0.default_headers.iter() { - // header map is empty - // TODO: probably append instead - req = req.insert_header_if_none(header); - } - req - } - - /// Create `ClientRequest` from `RequestHead` - /// - /// It is useful for proxy requests. This implementation - /// copies all headers and the method. - pub fn request_from(&self, url: U, head: &RequestHead) -> ClientRequest - where - Uri: TryFrom, - >::Error: Into, - { - let mut req = self.request(head.method.clone(), url); - for header in head.headers.iter() { - req = req.insert_header_if_none(header); - } - req - } - - /// Construct HTTP *GET* request. - pub fn get(&self, url: U) -> ClientRequest - where - Uri: TryFrom, - >::Error: Into, - { - self.request(Method::GET, url) - } - - /// Construct HTTP *HEAD* request. - pub fn head(&self, url: U) -> ClientRequest - where - Uri: TryFrom, - >::Error: Into, - { - self.request(Method::HEAD, url) - } - - /// Construct HTTP *PUT* request. - pub fn put(&self, url: U) -> ClientRequest - where - Uri: TryFrom, - >::Error: Into, - { - self.request(Method::PUT, url) - } - - /// Construct HTTP *POST* request. - pub fn post(&self, url: U) -> ClientRequest - where - Uri: TryFrom, - >::Error: Into, - { - self.request(Method::POST, url) - } - - /// Construct HTTP *PATCH* request. - pub fn patch(&self, url: U) -> ClientRequest - where - Uri: TryFrom, - >::Error: Into, - { - self.request(Method::PATCH, url) - } - - /// Construct HTTP *DELETE* request. - pub fn delete(&self, url: U) -> ClientRequest - where - Uri: TryFrom, - >::Error: Into, - { - self.request(Method::DELETE, url) - } - - /// Construct HTTP *OPTIONS* request. - pub fn options(&self, url: U) -> ClientRequest - where - Uri: TryFrom, - >::Error: Into, - { - self.request(Method::OPTIONS, url) - } - - /// Initialize a WebSocket connection. - /// Returns a WebSocket connection builder. - pub fn ws(&self, url: U) -> ws::WebsocketsRequest - where - Uri: TryFrom, - >::Error: Into, - { - let mut req = ws::WebsocketsRequest::new(url, self.0.clone()); - for (key, value) in self.0.default_headers.iter() { - req.head.headers.insert(key.clone(), value.clone()); - } - req - } - - /// Get default HeaderMap of Client. - /// - /// Returns Some(&mut HeaderMap) when Client object is unique - /// (No other clone of client exists at the same time). - pub fn headers(&mut self) -> Option<&mut HeaderMap> { - Rc::get_mut(&mut self.0.default_headers) - } -} diff --git a/awc/src/request.rs b/awc/src/request.rs index 3eb76e3f6..8824dd08a 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -12,10 +12,11 @@ use actix_http::{ }; use crate::{ + client::ClientConfig, error::{FreezeRequestError, InvalidUrl}, frozen::FrozenClientRequest, sender::{PrepForSendingError, RequestSender, SendClientRequest}, - BoxError, ClientConfig, + BoxError, }; #[cfg(feature = "cookies")] @@ -249,23 +250,16 @@ impl ClientRequest { /// Set a cookie /// /// ```no_run - /// use awc::{cookie, Client}; + /// use awc::{cookie::Cookie, Client}; /// /// # #[actix_rt::main] /// # async fn main() { - /// let resp = Client::new().get("https://www.rust-lang.org") - /// .cookie( - /// awc::cookie::Cookie::build("name", "value") - /// .domain("www.rust-lang.org") - /// .path("/") - /// .secure(true) - /// .http_only(true) - /// .finish(), - /// ) - /// .send() - /// .await; + /// let res = Client::new().get("https://httpbin.org/cookies") + /// .cookie(Cookie::new("name", "value")) + /// .send() + /// .await; /// - /// println!("Response: {:?}", resp); + /// println!("Response: {:?}", res); /// # } /// ``` #[cfg(feature = "cookies")] diff --git a/awc/src/sender.rs b/awc/src/sender.rs index 71d705d38..edf41163d 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -24,8 +24,9 @@ use actix_http::{encoding::Decoder, header::ContentEncoding, Payload}; use crate::{ any_body::AnyBody, + client::ClientConfig, error::{FreezeRequestError, InvalidUrl, SendRequestError}, - BoxError, ClientConfig, ClientResponse, ConnectRequest, ConnectResponse, + BoxError, ClientResponse, ConnectRequest, ConnectResponse, }; #[derive(Debug, From)] diff --git a/awc/src/ws.rs b/awc/src/ws.rs index c63e22969..96f8cf893 100644 --- a/awc/src/ws.rs +++ b/awc/src/ws.rs @@ -31,18 +31,19 @@ use std::{convert::TryFrom, fmt, net::SocketAddr, str}; use actix_codec::Framed; use actix_http::{ws, Payload, RequestHead}; use actix_rt::time::timeout; -use actix_service::Service; +use actix_service::Service as _; pub use actix_http::ws::{CloseCode, CloseReason, Codec, Frame, Message}; use crate::{ + client::ClientConfig, connect::{BoxedSocket, ConnectRequest}, error::{HttpError, InvalidUrl, SendRequestError, WsClientError}, http::{ header::{self, HeaderName, HeaderValue, TryIntoHeaderValue, AUTHORIZATION}, ConnectionType, Method, StatusCode, Uri, Version, }, - ClientConfig, ClientResponse, + ClientResponse, }; #[cfg(feature = "cookies")] From 01cbfc57244bc7c0528d158dbc61492ed65a64a3 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 25 Dec 2021 02:28:23 +0000 Subject: [PATCH 71/87] reduce -http re-exports in awc --- awc/src/error.rs | 1 + awc/src/lib.rs | 17 +++++++++++++---- src/http/mod.rs | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/awc/src/error.rs b/awc/src/error.rs index c1d855053..aa9dc4d99 100644 --- a/awc/src/error.rs +++ b/awc/src/error.rs @@ -1,5 +1,6 @@ //! HTTP client errors +// TODO: figure out how best to expose http::Error vs actix_http::Error pub use actix_http::{ error::{HttpError, PayloadError}, header::HeaderValue, diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 348d9312b..970ca2d92 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -105,6 +105,11 @@ #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] +pub use actix_http::body; + +#[cfg(feature = "cookies")] +pub use cookie; + mod any_body; mod builder; mod client; @@ -118,10 +123,14 @@ mod sender; pub mod test; pub mod ws; -// TODO: hmmmmmm -pub use actix_http as http; -#[cfg(feature = "cookies")] -pub use cookie; +pub mod http { + //! Various HTTP related types. + + // TODO: figure out how best to expose http::Error vs actix_http::Error + pub use actix_http::{ + header, uri, ConnectionType, Error, Method, StatusCode, Uri, Version, + }; +} pub use self::builder::ClientBuilder; pub use self::client::{Client, Connector}; diff --git a/src/http/mod.rs b/src/http/mod.rs index bbd94a60f..2581532cd 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -2,4 +2,5 @@ pub mod header; +// TODO: figure out how best to expose http::Error vs actix_http::Error pub use actix_http::{uri, ConnectionType, Error, Method, StatusCode, Uri, Version}; From 34e5c7c799b88dcd18ebaf8c4b8d75132e19bf25 Mon Sep 17 00:00:00 2001 From: Mark Lodato Date: Fri, 24 Dec 2021 21:35:19 -0500 Subject: [PATCH 72/87] Improve module docs for error handler middleware (#2543) --- src/middleware/err_handlers.rs | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/middleware/err_handlers.rs b/src/middleware/err_handlers.rs index 6d064372f..bde054330 100644 --- a/src/middleware/err_handlers.rs +++ b/src/middleware/err_handlers.rs @@ -37,27 +37,21 @@ type ErrorHandler = dyn Fn(ServiceResponse) -> Result(mut res: dev::ServiceResponse) -> Result> { -/// res.response_mut() -/// .headers_mut() -/// .insert(header::CONTENT_TYPE, header::HeaderValue::from_static("Error")); +/// use actix_web::http::{header, StatusCode}; +/// use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers}; +/// use actix_web::{dev, web, App, HttpResponse, Result}; /// +/// fn add_error_header(mut res: dev::ServiceResponse) -> Result> { +/// res.response_mut().headers_mut().insert( +/// header::CONTENT_TYPE, +/// header::HeaderValue::from_static("Error"), +/// ); /// Ok(ErrorHandlerResponse::Response(res.map_into_left_body())) /// } /// /// let app = App::new() -/// .wrap( -/// ErrorHandlers::new() -/// .handler(StatusCode::INTERNAL_SERVER_ERROR, render_500), -/// ) -/// .service(web::resource("/test") -/// .route(web::get().to(|| HttpResponse::Ok())) -/// .route(web::head().to(|| HttpResponse::MethodNotAllowed()) -/// )); +/// .wrap(ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, add_error_header)) +/// .service(web::resource("/").route(web::get().to(HttpResponse::InternalServerError))); /// ``` pub struct ErrorHandlers { handlers: Handlers, From adf993584124f44fc07835fcd7e467184291ab38 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 25 Dec 2021 03:44:09 +0000 Subject: [PATCH 73/87] improve scope documentation closes #2389 --- actix-router/src/resource.rs | 54 +++++++-------- src/resource.rs | 27 ++++---- src/route.rs | 6 +- src/scope.rs | 126 ++++++++++++++++------------------- src/web.rs | 13 +++- 5 files changed, 109 insertions(+), 117 deletions(-) diff --git a/actix-router/src/resource.rs b/actix-router/src/resource.rs index fa77b1e7b..f1eb9caf5 100644 --- a/actix-router/src/resource.rs +++ b/actix-router/src/resource.rs @@ -29,26 +29,25 @@ const REGEX_FLAGS: &str = "(?s-m)"; /// /// /// # Pattern Format and Matching Behavior -/// /// Resource pattern is defined as a string of zero or more _segments_ where each segment is /// preceded by a slash `/`. /// -/// This means that pattern string __must__ either be empty or begin with a slash (`/`). -/// This also implies that a trailing slash in pattern defines an empty segment. -/// For example, the pattern `"/user/"` has two segments: `["user", ""]` +/// This means that pattern string __must__ either be empty or begin with a slash (`/`). This also +/// implies that a trailing slash in pattern defines an empty segment. For example, the pattern +/// `"/user/"` has two segments: `["user", ""]` /// -/// A key point to underhand is that `ResourceDef` matches segments, not strings. -/// It matches segments individually. -/// For example, the pattern `/user/` is not considered a prefix for the path `/user/123/456`, -/// because the second segment doesn't match: `["user", ""]` vs `["user", "123", "456"]`. +/// A key point to understand is that `ResourceDef` matches segments, not strings. Segments are +/// matched individually. For example, the pattern `/user/` is not considered a prefix for the path +/// `/user/123/456`, because the second segment doesn't match: `["user", ""]` +/// vs `["user", "123", "456"]`. /// /// This definition is consistent with the definition of absolute URL path in -/// [RFC 3986 (section 3.3)](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) +/// [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) /// /// /// # Static Resources -/// A static resource is the most basic type of definition. Pass a pattern to -/// [new][Self::new]. Conforming paths must match the pattern exactly. +/// A static resource is the most basic type of definition. Pass a pattern to [new][Self::new]. +/// Conforming paths must match the pattern exactly. /// /// ## Examples /// ``` @@ -63,7 +62,6 @@ const REGEX_FLAGS: &str = "(?s-m)"; /// assert!(!resource.is_match("/search")); /// ``` /// -/// /// # Dynamic Segments /// Also known as "path parameters". Resources can define sections of a pattern that be extracted /// from a conforming path, if it conforms to (one of) the resource pattern(s). @@ -102,15 +100,15 @@ const REGEX_FLAGS: &str = "(?s-m)"; /// assert_eq!(path.get("id").unwrap(), "123"); /// ``` /// -/// /// # Prefix Resources /// A prefix resource is defined as pattern that can match just the start of a path, up to a /// segment boundary. /// /// Prefix patterns with a trailing slash may have an unexpected, though correct, behavior. -/// They define and therefore require an empty segment in order to match. Examples are given below. +/// They define and therefore require an empty segment in order to match. It is easier to understand +/// this behavior after reading the [matching behavior section]. Examples are given below. /// -/// Empty pattern matches any path as a prefix. +/// The empty pattern (`""`), as a prefix, matches any path. /// /// Prefix resources can contain dynamic segments. /// @@ -130,7 +128,6 @@ const REGEX_FLAGS: &str = "(?s-m)"; /// assert!(!resource.is_match("/user/123")); /// ``` /// -/// /// # Custom Regex Segments /// Dynamic segments can be customised to only match a specific regular expression. It can be /// helpful to do this if resource definitions would otherwise conflict and cause one to @@ -158,7 +155,6 @@ const REGEX_FLAGS: &str = "(?s-m)"; /// assert!(!resource.is_match("/user/abc")); /// ``` /// -/// /// # Tail Segments /// As a shortcut to defining a custom regex for matching _all_ remaining characters (not just those /// up until a `/` character), there is a special pattern to match (and capture) the remaining @@ -179,7 +175,6 @@ const REGEX_FLAGS: &str = "(?s-m)"; /// assert_eq!(path.get("tail").unwrap(), "main/LICENSE"); /// ``` /// -/// /// # Multi-Pattern Resources /// For resources that can map to multiple distinct paths, it may be suitable to use /// multi-pattern resources by passing an array/vec to [`new`][Self::new]. They will be combined @@ -198,7 +193,6 @@ const REGEX_FLAGS: &str = "(?s-m)"; /// assert!(resource.is_match("/index")); /// ``` /// -/// /// # Trailing Slashes /// It should be noted that this library takes no steps to normalize intra-path or trailing slashes. /// As such, all resource definitions implicitly expect a pre-processing step to normalize paths if @@ -212,6 +206,8 @@ const REGEX_FLAGS: &str = "(?s-m)"; /// assert!(!ResourceDef::new("/root/").is_match("/root")); /// assert!(!ResourceDef::prefix("/root/").is_match("/root")); /// ``` +/// +/// [matching behavior section]: #pattern-format-and-matching-behavior #[derive(Clone, Debug)] pub struct ResourceDef { id: u16, @@ -279,7 +275,7 @@ impl ResourceDef { /// ``` pub fn new(paths: T) -> Self { profile_method!(new); - Self::new2(paths, false) + Self::construct(paths, false) } /// Constructs a new resource definition using a pattern that performs prefix matching. @@ -292,7 +288,7 @@ impl ResourceDef { /// resource definition with a tail segment; use [`new`][Self::new] in this case. /// /// # Panics - /// Panics if path regex pattern is malformed. + /// Panics if path pattern is malformed. /// /// # Examples /// ``` @@ -307,14 +303,14 @@ impl ResourceDef { /// ``` pub fn prefix(paths: T) -> Self { profile_method!(prefix); - ResourceDef::new2(paths, true) + ResourceDef::construct(paths, true) } /// Constructs a new resource definition using a string pattern that performs prefix matching, - /// inserting a `/` to beginning of the pattern if absent and pattern is not empty. + /// ensuring a leading `/` if pattern is not empty. /// /// # Panics - /// Panics if path regex pattern is malformed. + /// Panics if path pattern is malformed. /// /// # Examples /// ``` @@ -515,8 +511,8 @@ impl ResourceDef { .collect::>(); match patterns.len() { - 1 => ResourceDef::new2(&patterns[0], other.is_prefix()), - _ => ResourceDef::new2(patterns, other.is_prefix()), + 1 => ResourceDef::construct(&patterns[0], other.is_prefix()), + _ => ResourceDef::construct(patterns, other.is_prefix()), } } @@ -881,8 +877,8 @@ impl ResourceDef { } } - fn new2(paths: T, is_prefix: bool) -> Self { - profile_method!(new2); + fn construct(paths: T, is_prefix: bool) -> Self { + profile_method!(construct); let patterns = paths.patterns(); let (pat_type, segments) = match &patterns { @@ -1814,7 +1810,7 @@ mod tests { #[test] #[should_panic] - fn prefix_plus_tail_match_is_allowed() { + fn prefix_plus_tail_match_disallowed() { ResourceDef::prefix("/user/{id}*"); } } diff --git a/src/resource.rs b/src/resource.rs index d94d2a464..0d82bb004 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -23,28 +23,25 @@ use crate::{ BoxError, Error, FromRequest, HttpResponse, Responder, }; -/// *Resource* is an entry in resources table which corresponds to requested URL. +/// A collection of [`Route`]s that respond to the same path pattern. /// -/// Resource in turn has at least one route. -/// Route consists of an handlers objects and list of guards -/// (objects that implement `Guard` trait). -/// Resources and routes uses builder-like pattern for configuration. -/// During request handling, resource object iterate through all routes -/// and check guards for specific route, if request matches all -/// guards, route considered matched and route handler get called. +/// Resource in turn has at least one route. Route consists of an handlers objects and list of +/// guards (objects that implement `Guard` trait). Resources and routes uses builder-like pattern +/// for configuration. During request handling, resource object iterate through all routes and check +/// guards for specific route, if request matches all guards, route considered matched and route +/// handler get called. /// +/// # Examples /// ``` /// use actix_web::{web, App, HttpResponse}; /// -/// fn main() { -/// let app = App::new().service( -/// web::resource("/") -/// .route(web::get().to(|| HttpResponse::Ok()))); -/// } +/// let app = App::new().service( +/// web::resource("/") +/// .route(web::get().to(|| HttpResponse::Ok()))); /// ``` /// -/// If no matching route could be found, *405* response code get returned. -/// Default behavior could be overridden with `default_resource()` method. +/// If no matching route could be found, *405* response code get returned. Default behavior could be +/// overridden with `default_resource()` method. pub struct Resource { endpoint: T, rdef: Patterns, diff --git a/src/route.rs b/src/route.rs index 4447bff50..16c01275b 100644 --- a/src/route.rs +++ b/src/route.rs @@ -15,10 +15,10 @@ use crate::{ BoxError, Error, FromRequest, HttpResponse, Responder, }; -/// Resource route definition +/// A request handler with [guards](guard). /// -/// Route uses builder-like pattern for configuration. -/// If handler is not explicitly set, default *404 Not Found* handler is used. +/// Route uses a builder-like pattern for configuration. If handler is not set, a `404 Not Found` +/// handler is used. pub struct Route { service: BoxedHttpServiceFactory, guards: Rc>>, diff --git a/src/scope.rs b/src/scope.rs index 1fd282f61..c3bab8f7e 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -27,34 +27,36 @@ use crate::{ type Guards = Vec>; -/// Resources scope. +/// A collection of [`Route`]s, [`Resource`]s, or other services that share a common path prefix. /// -/// Scope is a set of resources with common root path. -/// Scopes collect multiple paths under a common path prefix. -/// Scope path can contain variable path segments as resources. -/// Scope prefix is always complete path segment, i.e `/app` would -/// be converted to a `/app/` and it would not match `/app` path. +/// The `Scope`'s path can contain [dynamic segments]. The dynamic segments can be extracted from +/// requests using the [`Path`](crate::web::Path) extractor or +/// with [`HttpRequest::match_info()`](crate::HttpRequest::match_info). /// -/// You can get variable path segments from `HttpRequest::match_info()`. -/// `Path` extractor also is able to extract scope level variable segments. +/// # Avoid Trailing Slashes +/// Avoid using trailing slashes in the scope prefix (e.g., `web::scope("/scope/")`). It will almost +/// certainly not have the expected behavior. See the [documentation on resource definitions][pat] +/// to understand why this is the case and how to correctly construct scope/prefix definitions. /// +/// # Examples /// ``` /// use actix_web::{web, App, HttpResponse}; /// -/// fn main() { -/// let app = App::new().service( -/// web::scope("/{project_id}/") -/// .service(web::resource("/path1").to(|| async { "OK" })) -/// .service(web::resource("/path2").route(web::get().to(|| HttpResponse::Ok()))) -/// .service(web::resource("/path3").route(web::head().to(HttpResponse::MethodNotAllowed))) -/// ); -/// } +/// let app = App::new().service( +/// web::scope("/{project_id}/") +/// .service(web::resource("/path1").to(|| async { "OK" })) +/// .service(web::resource("/path2").route(web::get().to(|| HttpResponse::Ok()))) +/// .service(web::resource("/path3").route(web::head().to(HttpResponse::MethodNotAllowed))) +/// ); /// ``` /// /// In the above example three routes get registered: -/// * /{project_id}/path1 - responds to all http method -/// * /{project_id}/path2 - `GET` requests -/// * /{project_id}/path3 - `HEAD` requests +/// - /{project_id}/path1 - responds to all HTTP methods +/// - /{project_id}/path2 - responds to `GET` requests +/// - /{project_id}/path3 - responds to `HEAD` requests +/// +/// [pat]: crate::dev::ResourceDef#prefix-resources +/// [dynamic segments]: crate::dev::ResourceDef#dynamic-segments pub struct Scope { endpoint: T, rdef: String, @@ -106,16 +108,14 @@ where /// "Welcome!" /// } /// - /// fn main() { - /// let app = App::new().service( - /// web::scope("/app") - /// .guard(guard::Header("content-type", "text/plain")) - /// .route("/test1", web::get().to(index)) - /// .route("/test2", web::post().to(|r: HttpRequest| { - /// HttpResponse::MethodNotAllowed() - /// })) - /// ); - /// } + /// let app = App::new().service( + /// web::scope("/app") + /// .guard(guard::Header("content-type", "text/plain")) + /// .route("/test1", web::get().to(index)) + /// .route("/test2", web::post().to(|r: HttpRequest| { + /// HttpResponse::MethodNotAllowed() + /// })) + /// ); /// ``` pub fn guard(mut self, guard: G) -> Self { self.guards.push(Box::new(guard)); @@ -186,15 +186,13 @@ where /// ); /// } /// - /// fn main() { - /// let app = App::new() - /// .wrap(middleware::Logger::default()) - /// .service( - /// web::scope("/api") - /// .configure(config) - /// ) - /// .route("/index.html", web::get().to(|| HttpResponse::Ok())); - /// } + /// let app = App::new() + /// .wrap(middleware::Logger::default()) + /// .service( + /// web::scope("/api") + /// .configure(config) + /// ) + /// .route("/index.html", web::get().to(|| HttpResponse::Ok())); /// ``` pub fn configure(mut self, cfg_fn: F) -> Self where @@ -233,13 +231,11 @@ where /// "Welcome!" /// } /// - /// fn main() { - /// let app = App::new().service( - /// web::scope("/app").service( - /// web::scope("/v1") - /// .service(web::resource("/test1").to(index))) - /// ); - /// } + /// let app = App::new().service( + /// web::scope("/app").service( + /// web::scope("/v1") + /// .service(web::resource("/test1").to(index))) + /// ); /// ``` pub fn service(mut self, factory: F) -> Self where @@ -263,13 +259,11 @@ where /// "Welcome!" /// } /// - /// fn main() { - /// let app = App::new().service( - /// web::scope("/app") - /// .route("/test1", web::get().to(index)) - /// .route("/test2", web::post().to(|| HttpResponse::MethodNotAllowed())) - /// ); - /// } + /// let app = App::new().service( + /// web::scope("/app") + /// .route("/test1", web::get().to(index)) + /// .route("/test2", web::post().to(|| HttpResponse::MethodNotAllowed())) + /// ); /// ``` pub fn route(self, path: &str, mut route: Route) -> Self { self.service( @@ -355,21 +349,19 @@ where /// "Welcome!" /// } /// - /// fn main() { - /// let app = App::new().service( - /// web::scope("/app") - /// .wrap_fn(|req, srv| { - /// let fut = srv.call(req); - /// async { - /// let mut res = fut.await?; - /// res.headers_mut().insert( - /// CONTENT_TYPE, HeaderValue::from_static("text/plain"), - /// ); - /// Ok(res) - /// } - /// }) - /// .route("/index.html", web::get().to(index))); - /// } + /// let app = App::new().service( + /// web::scope("/app") + /// .wrap_fn(|req, srv| { + /// let fut = srv.call(req); + /// async { + /// let mut res = fut.await?; + /// res.headers_mut().insert( + /// CONTENT_TYPE, HeaderValue::from_static("text/plain"), + /// ); + /// Ok(res) + /// } + /// }) + /// .route("/index.html", web::get().to(index))); /// ``` pub fn wrap_fn( self, diff --git a/src/web.rs b/src/web.rs index 042b8a008..22877692d 100644 --- a/src/web.rs +++ b/src/web.rs @@ -52,11 +52,16 @@ pub fn resource(path: T) -> Resource { /// Scopes collect multiple paths under a common path prefix. The scope's path can contain dynamic /// path segments. /// +/// # Avoid Trailing Slashes +/// Avoid using trailing slashes in the scope prefix (e.g., `web::scope("/scope/")`). It will almost +/// certainly not have the expected behavior. See the [documentation on resource definitions][pat] +/// to understand why this is the case and how to correctly construct scope/prefix definitions. +/// /// # Examples /// In this example, three routes are set up (and will handle any method): -/// * `/{project_id}/path1` -/// * `/{project_id}/path2` -/// * `/{project_id}/path3` +/// - `/{project_id}/path1` +/// - `/{project_id}/path2` +/// - `/{project_id}/path3` /// /// ``` /// use actix_web::{web, App, HttpResponse}; @@ -68,6 +73,8 @@ pub fn resource(path: T) -> Resource { /// .service(web::resource("/path3").to(|| HttpResponse::MethodNotAllowed())) /// ); /// ``` +/// +/// [pat]: crate::dev::ResourceDef#prefix-resources pub fn scope(path: &str) -> Scope { Scope::new(path) } From 5860fe53814ede54d3f6939af1a457a9b9549613 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 25 Dec 2021 04:43:59 +0000 Subject: [PATCH 74/87] expose Handler trait --- src/handler.rs | 99 ++++++++++++++++++++++++++++++++++++++++--------- src/lib.rs | 1 + src/resource.rs | 6 +-- src/route.rs | 6 +-- src/web.rs | 6 +-- 5 files changed, 91 insertions(+), 27 deletions(-) diff --git a/src/handler.rs b/src/handler.rs index e543ecc7f..647606890 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -8,26 +8,88 @@ use crate::{ BoxError, FromRequest, HttpResponse, Responder, }; -/// A request handler is an async function that accepts zero or more parameters that can be -/// extracted from a request (i.e., [`impl FromRequest`]) and returns a type that can be converted -/// into an [`HttpResponse`] (that is, it impls the [`Responder`] trait). +/// The interface for request handlers. /// -/// If you got the error `the trait Handler<_, _, _> is not implemented`, then your function is not -/// a valid handler. See for more information. +/// # What Is A Request Handler +/// A request handler has three requirements: +/// 1. It is an async function (or a function/closure that returns an appropriate future); +/// 1. The function accepts zero or more parameters that implement [`FromRequest`]; +/// 1. The async function (or future) resolves to a type that can be converted into an +/// [`HttpResponse`] (i.e., it implements the [`Responder`] trait). /// -/// [`impl FromRequest`]: crate::FromRequest +/// # Compiler Errors +/// If you get the error `the trait Handler<_, _, _> is not implemented`, then your handler does not +/// fulfill one or more of the above requirements. +/// +/// Unfortunately we cannot provide a better compile error message (while keeping the trait's +/// flexibility) unless a stable alternative to [`#[rustc_on_unimplemented]`][on_unimpl] is added +/// to Rust. +/// +/// # How Do Handlers Receive Variable Numbers Of Arguments +/// Rest assured there is no macro magic here; it's just traits. +/// +/// The first thing to note is that [`FromRequest`] is implemented for tuples (up to 12 in length). +/// +/// Secondly, the `Handler` trait is implemented for functions (up to an [arity] of 12) in a way +/// that aligns their parameter positions with a corresponding tuple of types (becoming the `T` type +/// parameter in this trait's implementation). +/// +/// Thanks to Rust's type system, Actix Web can infer the function parameter types. During the +/// extraction step, the parameter types are described as a tuple type, [`from_request`] is run on +/// that tuple, and the `Handler::call` implementation for that particular function arity +/// destructures the tuple into it's component types and calls your handler function with them. +/// +/// In pseudo-code the process looks something like this: +/// ```ignore +/// async fn my_handler(body: String, state: web::Data) -> impl Responder { +/// ... +/// } +/// +/// // the function params above described as a tuple, names do not matter, only position +/// type InferredMyHandlerArgs = (String, web::Data); +/// +/// // create tuple of arguments to be passed to handler +/// let args = InferredMyHandlerArgs::from_request(&request, &payload).await; +/// +/// // call handler with argument tuple +/// let response = Handler::call(&my_handler, args).await; +/// +/// // which is effectively... +/// +/// let (body, state) = args; +/// let response = my_handler(body, state).await; +/// ``` +/// +/// This is the source code for the 2-parameter implementation of `Handler` to help illustrate the +/// bounds of the handler call after argument extraction: +/// ```ignore +/// impl Handler<(Arg1, Arg2), R> for Func +/// where +/// Func: Fn(Arg1, Arg2) -> R + Clone + 'static, +/// R: Future, +/// R::Output: Responder, +/// { +/// fn call(&self, (arg1, arg2): (Arg1, Arg2)) -> R { +/// (self)(arg1, arg2) +/// } +/// } +/// ``` +/// +/// [arity]: https://en.wikipedia.org/wiki/Arity +/// [`from_request`]: FromRequest::from_request +/// [on_unimpl]: https://github.com/rust-lang/rust/issues/29628 pub trait Handler: Clone + 'static where R: Future, R::Output: Responder, { - fn call(&self, param: T) -> R; + fn call(&self, args: T) -> R; } -pub(crate) fn handler_service(handler: F) -> BoxedHttpServiceFactory +pub(crate) fn handler_service(handler: F) -> BoxedHttpServiceFactory where - F: Handler, - T: FromRequest, + F: Handler, + Args: FromRequest, R: Future, R::Output: Responder, ::Body: MessageBody, @@ -39,7 +101,7 @@ where async move { let (req, mut payload) = req.into_parts(); - let res = match T::from_request(&req, &mut payload).await { + let res = match Args::from_request(&req, &mut payload).await { Err(err) => HttpResponse::from_error(err), Ok(data) => handler @@ -59,17 +121,18 @@ where /// /// # Examples /// ```ignore -/// factory_tuple! {} // implements Handler for types: fn() -> Res -/// factory_tuple! { A B C } // implements Handler for types: fn(A, B, C) -> Res +/// factory_tuple! {} // implements Handler for types: fn() -> R +/// factory_tuple! { A B C } // implements Handler for types: fn(A, B, C) -> R /// ``` macro_rules! factory_tuple ({ $($param:ident)* } => { - impl Handler<($($param,)*), Res> for Func - where Func: Fn($($param),*) -> Res + Clone + 'static, - Res: Future, - Res::Output: Responder, + impl Handler<($($param,)*), R> for Func + where Func: Fn($($param),*) -> R + Clone + 'static, + R: Future, + R::Output: Responder, { + #[inline] #[allow(non_snake_case)] - fn call(&self, ($($param,)*): ($($param,)*)) -> Res { + fn call(&self, ($($param,)*): ($($param,)*)) -> R { (self)($($param,)*) } } diff --git a/src/lib.rs b/src/lib.rs index 171a2d101..3462ae90b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,6 +106,7 @@ pub use cookie; pub use crate::app::App; pub use crate::error::{Error, ResponseError, Result}; pub use crate::extract::FromRequest; +pub use crate::handler::Handler; pub use crate::request::HttpRequest; pub use crate::resource::Resource; pub use crate::response::{CustomizeResponder, HttpResponse, HttpResponseBuilder, Responder}; diff --git a/src/resource.rs b/src/resource.rs index 0d82bb004..c6c8a5b89 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -232,10 +232,10 @@ where /// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() } /// App::new().service(web::resource("/").route(web::route().to(index))); /// ``` - pub fn to(mut self, handler: F) -> Self + pub fn to(mut self, handler: F) -> Self where - F: Handler, - I: FromRequest + 'static, + F: Handler, + Args: FromRequest + 'static, R: Future + 'static, R::Output: Responder + 'static, ::Body: MessageBody, diff --git a/src/route.rs b/src/route.rs index 16c01275b..6396c4286 100644 --- a/src/route.rs +++ b/src/route.rs @@ -176,10 +176,10 @@ impl Route { /// ); /// } /// ``` - pub fn to(mut self, handler: F) -> Self + pub fn to(mut self, handler: F) -> Self where - F: Handler, - T: FromRequest + 'static, + F: Handler, + Args: FromRequest + 'static, R: Future + 'static, R::Output: Responder + 'static, ::Body: MessageBody, diff --git a/src/web.rs b/src/web.rs index 22877692d..46e7704b6 100644 --- a/src/web.rs +++ b/src/web.rs @@ -146,10 +146,10 @@ pub fn method(method: Method) -> Route { /// web::to(index)) /// ); /// ``` -pub fn to(handler: F) -> Route +pub fn to(handler: F) -> Route where - F: Handler, - I: FromRequest + 'static, + F: Handler, + Args: FromRequest + 'static, R: Future + 'static, R::Output: Responder + 'static, ::Body: MessageBody + 'static, From 2e493cf7915587f157f6da42b1a0dfaa34d7f097 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sat, 25 Dec 2021 04:53:51 +0000 Subject: [PATCH 75/87] remove crate level clippy allows --- src/app.rs | 42 +++++++++++++-------------- src/app_service.rs | 1 + src/config.rs | 2 ++ src/guard.rs | 58 ++++++++++++++++---------------------- src/handler.rs | 8 +++--- src/lib.rs | 1 - src/middleware/compress.rs | 1 + src/resource.rs | 16 +++++------ src/scope.rs | 1 + src/server.rs | 1 + 10 files changed, 61 insertions(+), 70 deletions(-) diff --git a/src/app.rs b/src/app.rs index b4b952734..10868d18d 100644 --- a/src/app.rs +++ b/src/app.rs @@ -122,9 +122,10 @@ impl App { self.app_data(Data::new(data)) } - /// Add application data factory. This function is similar to `.data()` but it accepts a - /// "data factory". Data values are constructed asynchronously during application - /// initialization, before the server starts accepting requests. + /// Add application data factory that resolves asynchronously. + /// + /// Data items are constructed during application initialization, before the server starts + /// accepting requests. pub fn data_factory(mut self, data: F) -> Self where F: Fn() -> Out + 'static, @@ -150,6 +151,7 @@ impl App { } .boxed_local() })); + self } @@ -200,11 +202,9 @@ impl App { /// "Welcome!" /// } /// - /// fn main() { - /// let app = App::new() - /// .route("/test1", web::get().to(index)) - /// .route("/test2", web::post().to(|| HttpResponse::MethodNotAllowed())); - /// } + /// let app = App::new() + /// .route("/test1", web::get().to(index)) + /// .route("/test2", web::post().to(|| HttpResponse::MethodNotAllowed())); /// ``` pub fn route(self, path: &str, mut route: Route) -> Self { self.service( @@ -243,13 +243,11 @@ impl App { /// "Welcome!" /// } /// - /// fn main() { - /// let app = App::new() - /// .service( - /// web::resource("/index.html").route(web::get().to(index))) - /// .default_service( - /// web::route().to(|| HttpResponse::NotFound())); - /// } + /// let app = App::new() + /// .service( + /// web::resource("/index.html").route(web::get().to(index))) + /// .default_service( + /// web::route().to(|| HttpResponse::NotFound())); /// ``` /// /// It is also possible to use static files as default service. @@ -257,14 +255,12 @@ impl App { /// ``` /// use actix_web::{web, App, HttpResponse}; /// - /// fn main() { - /// let app = App::new() - /// .service( - /// web::resource("/index.html").to(|| HttpResponse::Ok())) - /// .default_service( - /// web::to(|| HttpResponse::NotFound()) - /// ); - /// } + /// let app = App::new() + /// .service( + /// web::resource("/index.html").to(|| HttpResponse::Ok())) + /// .default_service( + /// web::to(|| HttpResponse::NotFound()) + /// ); /// ``` pub fn default_service(mut self, svc: F) -> Self where diff --git a/src/app_service.rs b/src/app_service.rs index 4e84cb201..e0d424390 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -236,6 +236,7 @@ where } pub struct AppRoutingFactory { + #[allow(clippy::type_complexity)] services: Rc< [( ResourceDef, diff --git a/src/config.rs b/src/config.rs index 9e77c0f96..cfa9a4ca3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,6 +24,7 @@ pub struct AppService { config: AppConfig, root: bool, default: Rc, + #[allow(clippy::type_complexity)] services: Vec<( ResourceDef, HttpNewService, @@ -48,6 +49,7 @@ impl AppService { self.root } + #[allow(clippy::type_complexity)] pub(crate) fn into_services( self, ) -> ( diff --git a/src/guard.rs b/src/guard.rs index d5c585c1b..db7f06987 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -15,14 +15,12 @@ //! ``` //! use actix_web::{web, http, dev, guard, App, HttpResponse}; //! -//! fn main() { -//! App::new().service(web::resource("/index.html").route( -//! web::route() -//! .guard(guard::Post()) -//! .guard(guard::fn_guard(|head| head.method == http::Method::GET)) -//! .to(|| HttpResponse::MethodNotAllowed())) -//! ); -//! } +//! App::new().service(web::resource("/index.html").route( +//! web::route() +//! .guard(guard::Post()) +//! .guard(guard::fn_guard(|head| head.method == http::Method::GET)) +//! .to(|| HttpResponse::MethodNotAllowed())) +//! ); //! ``` #![allow(non_snake_case)] @@ -53,16 +51,14 @@ impl Guard for Rc { /// ``` /// use actix_web::{guard, web, App, HttpResponse}; /// -/// fn main() { -/// App::new().service(web::resource("/index.html").route( -/// web::route() -/// .guard( -/// guard::fn_guard( -/// |req| req.headers() -/// .contains_key("content-type"))) -/// .to(|| HttpResponse::MethodNotAllowed())) -/// ); -/// } +/// App::new().service(web::resource("/index.html").route( +/// web::route() +/// .guard( +/// guard::fn_guard( +/// |req| req.headers() +/// .contains_key("content-type"))) +/// .to(|| HttpResponse::MethodNotAllowed())) +/// ); /// ``` pub fn fn_guard(f: F) -> impl Guard where @@ -96,13 +92,11 @@ where /// ``` /// use actix_web::{web, guard, App, HttpResponse}; /// -/// fn main() { -/// App::new().service(web::resource("/index.html").route( -/// web::route() -/// .guard(guard::Any(guard::Get()).or(guard::Post())) -/// .to(|| HttpResponse::MethodNotAllowed())) -/// ); -/// } +/// App::new().service(web::resource("/index.html").route( +/// web::route() +/// .guard(guard::Any(guard::Get()).or(guard::Post())) +/// .to(|| HttpResponse::MethodNotAllowed())) +/// ); /// ``` pub fn Any(guard: F) -> AnyGuard { AnyGuard(vec![Box::new(guard)]) @@ -135,14 +129,12 @@ impl Guard for AnyGuard { /// ``` /// use actix_web::{guard, web, App, HttpResponse}; /// -/// fn main() { -/// App::new().service(web::resource("/index.html").route( -/// web::route() -/// .guard( -/// guard::All(guard::Get()).and(guard::Header("content-type", "text/plain"))) -/// .to(|| HttpResponse::MethodNotAllowed())) -/// ); -/// } +/// App::new().service(web::resource("/index.html").route( +/// web::route() +/// .guard( +/// guard::All(guard::Get()).and(guard::Header("content-type", "text/plain"))) +/// .to(|| HttpResponse::MethodNotAllowed())) +/// ); /// ``` pub fn All(guard: F) -> AllGuard { AllGuard(vec![Box::new(guard)]) diff --git a/src/handler.rs b/src/handler.rs index 647606890..ea6855c7f 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -31,8 +31,8 @@ use crate::{ /// The first thing to note is that [`FromRequest`] is implemented for tuples (up to 12 in length). /// /// Secondly, the `Handler` trait is implemented for functions (up to an [arity] of 12) in a way -/// that aligns their parameter positions with a corresponding tuple of types (becoming the `T` type -/// parameter in this trait's implementation). +/// that aligns their parameter positions with a corresponding tuple of types (becoming the `Args` +/// type parameter for this trait). /// /// Thanks to Rust's type system, Actix Web can infer the function parameter types. During the /// extraction step, the parameter types are described as a tuple type, [`from_request`] is run on @@ -78,12 +78,12 @@ use crate::{ /// [arity]: https://en.wikipedia.org/wiki/Arity /// [`from_request`]: FromRequest::from_request /// [on_unimpl]: https://github.com/rust-lang/rust/issues/29628 -pub trait Handler: Clone + 'static +pub trait Handler: Clone + 'static where R: Future, R::Output: Responder, { - fn call(&self, args: T) -> R; + fn call(&self, args: Args) -> R; } pub(crate) fn handler_service(handler: F) -> BoxedHttpServiceFactory diff --git a/src/lib.rs b/src/lib.rs index 3462ae90b..5f5b915b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,7 +66,6 @@ #![deny(rust_2018_idioms, nonstandard_style)] #![warn(future_incompatible)] -#![allow(clippy::needless_doctest_main, clippy::type_complexity)] #![doc(html_logo_url = "https://actix.rs/img/logo.png")] #![doc(html_favicon_url = "https://actix.rs/favicon.ico")] diff --git a/src/middleware/compress.rs b/src/middleware/compress.rs index af4a107e3..d3cdf5763 100644 --- a/src/middleware/compress.rs +++ b/src/middleware/compress.rs @@ -113,6 +113,7 @@ where { type Response = ServiceResponse>>; type Error = Error; + #[allow(clippy::type_complexity)] type Future = Either, Ready>>; actix_service::forward_ready!(service); diff --git a/src/resource.rs b/src/resource.rs index c6c8a5b89..f0c6f6d7c 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -131,15 +131,13 @@ where /// ``` /// use actix_web::{web, guard, App, HttpResponse}; /// - /// fn main() { - /// let app = App::new().service( - /// web::resource("/").route( - /// web::route() - /// .guard(guard::Any(guard::Get()).or(guard::Put())) - /// .guard(guard::Header("Content-Type", "text/plain")) - /// .to(|| HttpResponse::Ok())) - /// ); - /// } + /// let app = App::new().service( + /// web::resource("/").route( + /// web::route() + /// .guard(guard::Any(guard::Get()).or(guard::Put())) + /// .guard(guard::Header("Content-Type", "text/plain")) + /// .to(|| HttpResponse::Ok())) + /// ); /// ``` /// /// Multiple routes could be added to a resource. Resource object uses diff --git a/src/scope.rs b/src/scope.rs index c3bab8f7e..176e0d5a0 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -469,6 +469,7 @@ where } pub struct ScopeFactory { + #[allow(clippy::type_complexity)] services: Rc< [( ResourceDef, diff --git a/src/server.rs b/src/server.rs index b2ff423f1..ed0c965b3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -63,6 +63,7 @@ where backlog: u32, sockets: Vec, builder: ServerBuilder, + #[allow(clippy::type_complexity)] on_connect_fn: Option>, _phantom: PhantomData<(S, B)>, } From ac0c4eb68434a975beacfca20e26cf2596999f5f Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 26 Dec 2021 21:24:03 +0000 Subject: [PATCH 76/87] update actix-tls references to stable 3.0.0 --- Cargo.toml | 2 +- actix-http-test/Cargo.toml | 2 +- actix-http/Cargo.toml | 4 ++-- awc/Cargo.toml | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d15f26172..7b095af91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,7 +75,7 @@ actix-rt = "2.3" actix-server = "2.0.0-rc.1" actix-service = "2.0.0" actix-utils = "3.0.0" -actix-tls = { version = "3.0.0-rc.1", default-features = false, optional = true } +actix-tls = { version = "3.0.0", default-features = false, optional = true } actix-http = "3.0.0-beta.16" actix-router = "0.5.0-beta.3" diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index e1c875a1f..2ad620b08 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -31,7 +31,7 @@ openssl = ["tls-openssl", "awc/openssl"] [dependencies] actix-service = "2.0.0" actix-codec = "0.4.1" -actix-tls = "3.0.0-rc.1" +actix-tls = "3.0.0" actix-utils = "3.0.0" actix-rt = "2.2" actix-server = "2.0.0-rc.1" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index c15f5ee28..3fa437562 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -71,7 +71,7 @@ sha-1 = "0.10" smallvec = "1.6.1" # tls -actix-tls = { version = "3.0.0-rc.1", default-features = false, optional = true } +actix-tls = { version = "3.0.0", default-features = false, optional = true } # compression brotli2 = { version="0.3.2", optional = true } @@ -81,7 +81,7 @@ zstd = { version = "0.9", optional = true } [dev-dependencies] actix-http-test = { version = "3.0.0-beta.9", features = ["openssl"] } actix-server = "2.0.0-rc.1" -actix-tls = { version = "3.0.0-rc.1", features = ["openssl"] } +actix-tls = { version = "3.0.0", features = ["openssl"] } actix-web = "4.0.0-beta.15" async-stream = "0.3" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 4b29aac16..d0494e023 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -62,7 +62,7 @@ actix-codec = "0.4.1" actix-service = "2.0.0" actix-http = "3.0.0-beta.16" actix-rt = { version = "2.1", default-features = false } -actix-tls = { version = "3.0.0-rc.2", features = ["connect", "uri"] } +actix-tls = { version = "3.0.0", features = ["connect", "uri"] } actix-utils = "3.0.0" ahash = "0.7" @@ -97,7 +97,7 @@ actix-http = { version = "3.0.0-beta.16", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.9", features = ["openssl"] } actix-server = "2.0.0-rc.1" actix-test = { version = "0.1.0-beta.9", features = ["openssl", "rustls"] } -actix-tls = { version = "3.0.0-rc.1", features = ["openssl", "rustls"] } +actix-tls = { version = "3.0.0", features = ["openssl", "rustls"] } actix-utils = "3.0.0" actix-web = { version = "4.0.0-beta.15", features = ["openssl"] } From 554ae7a868b169b9210c9c24002ca1afd96f0c71 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Mon, 27 Dec 2021 03:44:30 +0300 Subject: [PATCH 77/87] rework Handler trait (#2549) --- src/handler.rs | 37 +++++++++++++++++-------------------- src/resource.rs | 11 ++++------- src/route.rs | 14 +++++--------- src/web.rs | 15 ++++++--------- 4 files changed, 32 insertions(+), 45 deletions(-) diff --git a/src/handler.rs b/src/handler.rs index ea6855c7f..d458e22e1 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -3,9 +3,8 @@ use std::future::Future; use actix_service::{boxed, fn_service}; use crate::{ - body::MessageBody, service::{BoxedHttpServiceFactory, ServiceRequest, ServiceResponse}, - BoxError, FromRequest, HttpResponse, Responder, + FromRequest, HttpResponse, Responder, }; /// The interface for request handlers. @@ -18,7 +17,7 @@ use crate::{ /// [`HttpResponse`] (i.e., it implements the [`Responder`] trait). /// /// # Compiler Errors -/// If you get the error `the trait Handler<_, _, _> is not implemented`, then your handler does not +/// If you get the error `the trait Handler<_> is not implemented`, then your handler does not /// fulfill one or more of the above requirements. /// /// Unfortunately we cannot provide a better compile error message (while keeping the trait's @@ -78,22 +77,18 @@ use crate::{ /// [arity]: https://en.wikipedia.org/wiki/Arity /// [`from_request`]: FromRequest::from_request /// [on_unimpl]: https://github.com/rust-lang/rust/issues/29628 -pub trait Handler: Clone + 'static -where - R: Future, - R::Output: Responder, -{ - fn call(&self, args: Args) -> R; +pub trait Handler: Clone + 'static { + type Output; + type Future: Future; + + fn call(&self, args: Args) -> Self::Future; } -pub(crate) fn handler_service(handler: F) -> BoxedHttpServiceFactory +pub(crate) fn handler_service(handler: F) -> BoxedHttpServiceFactory where - F: Handler, + F: Handler, Args: FromRequest, - R: Future, - R::Output: Responder, - ::Body: MessageBody, - <::Body as MessageBody>::Error: Into, + F::Output: Responder, { boxed::factory(fn_service(move |req: ServiceRequest| { let handler = handler.clone(); @@ -125,14 +120,16 @@ where /// factory_tuple! { A B C } // implements Handler for types: fn(A, B, C) -> R /// ``` macro_rules! factory_tuple ({ $($param:ident)* } => { - impl Handler<($($param,)*), R> for Func - where Func: Fn($($param),*) -> R + Clone + 'static, - R: Future, - R::Output: Responder, + impl Handler<($($param,)*)> for Func + where Func: Fn($($param),*) -> Fut + Clone + 'static, + Fut: Future, { + type Output = Fut::Output; + type Future = Fut; + #[inline] #[allow(non_snake_case)] - fn call(&self, ($($param,)*): ($($param,)*)) -> R { + fn call(&self, ($($param,)*): ($($param,)*)) -> Self::Future { (self)($($param,)*) } } diff --git a/src/resource.rs b/src/resource.rs index f0c6f6d7c..564c4d3ef 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -20,7 +20,7 @@ use crate::{ BoxedHttpService, BoxedHttpServiceFactory, HttpServiceFactory, ServiceRequest, ServiceResponse, }, - BoxError, Error, FromRequest, HttpResponse, Responder, + Error, FromRequest, HttpResponse, Responder, }; /// A collection of [`Route`]s that respond to the same path pattern. @@ -230,14 +230,11 @@ where /// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() } /// App::new().service(web::resource("/").route(web::route().to(index))); /// ``` - pub fn to(mut self, handler: F) -> Self + pub fn to(mut self, handler: F) -> Self where - F: Handler, + F: Handler, Args: FromRequest + 'static, - R: Future + 'static, - R::Output: Responder + 'static, - ::Body: MessageBody, - <::Body as MessageBody>::Error: Into, + F::Output: Responder + 'static, { self.routes.push(Route::new().to(handler)); self diff --git a/src/route.rs b/src/route.rs index 6396c4286..6d6fca4b7 100644 --- a/src/route.rs +++ b/src/route.rs @@ -1,4 +1,4 @@ -use std::{future::Future, mem, rc::Rc}; +use std::{mem, rc::Rc}; use actix_http::Method; use actix_service::{ @@ -8,11 +8,10 @@ use actix_service::{ use futures_core::future::LocalBoxFuture; use crate::{ - body::MessageBody, guard::{self, Guard}, handler::{handler_service, Handler}, service::{BoxedHttpServiceFactory, ServiceRequest, ServiceResponse}, - BoxError, Error, FromRequest, HttpResponse, Responder, + Error, FromRequest, HttpResponse, Responder, }; /// A request handler with [guards](guard). @@ -176,14 +175,11 @@ impl Route { /// ); /// } /// ``` - pub fn to(mut self, handler: F) -> Self + pub fn to(mut self, handler: F) -> Self where - F: Handler, + F: Handler, Args: FromRequest + 'static, - R: Future + 'static, - R::Output: Responder + 'static, - ::Body: MessageBody, - <::Body as MessageBody>::Error: Into, + F::Output: Responder + 'static, { self.service = handler_service(handler); self diff --git a/src/web.rs b/src/web.rs index 46e7704b6..47bff36a3 100644 --- a/src/web.rs +++ b/src/web.rs @@ -1,14 +1,14 @@ //! Essentials helper functions and types for application registration. -use std::{error::Error as StdError, future::Future}; +use std::future::Future; use actix_http::Method; use actix_router::IntoPatterns; pub use bytes::{Buf, BufMut, Bytes, BytesMut}; use crate::{ - body::MessageBody, error::BlockingError, extract::FromRequest, handler::Handler, - resource::Resource, route::Route, scope::Scope, service::WebService, Responder, + error::BlockingError, extract::FromRequest, handler::Handler, resource::Resource, + route::Route, scope::Scope, service::WebService, Responder, }; pub use crate::config::ServiceConfig; @@ -146,14 +146,11 @@ pub fn method(method: Method) -> Route { /// web::to(index)) /// ); /// ``` -pub fn to(handler: F) -> Route +pub fn to(handler: F) -> Route where - F: Handler, + F: Handler, Args: FromRequest + 'static, - R: Future + 'static, - R::Output: Responder + 'static, - ::Body: MessageBody + 'static, - <::Body as MessageBody>::Error: Into>, + F::Output: Responder + 'static, { Route::new().to(handler) } From 2308f8afa4b71dad726d5a3534e7eb4b1469098a Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 27 Dec 2021 16:15:20 +0000 Subject: [PATCH 78/87] use const header values where possible --- actix-http/src/h2/dispatcher.rs | 8 +++++--- actix-http/src/header/shared/content_encoding.rs | 4 ++-- actix-http/src/ws/mod.rs | 5 +++-- awc/src/client/h2proto.rs | 8 +++++--- awc/src/ws.rs | 13 ++++++++----- src/middleware/default_headers.rs | 7 +++---- 6 files changed, 26 insertions(+), 19 deletions(-) diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 7f0f15ee6..a90eb3466 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -288,9 +288,11 @@ fn prepare_response( let _ = match size { BodySize::None | BodySize::Stream => None, - BodySize::Sized(0) => res - .headers_mut() - .insert(CONTENT_LENGTH, HeaderValue::from_static("0")), + BodySize::Sized(0) => { + #[allow(clippy::declare_interior_mutable_const)] + const HV_ZERO: HeaderValue = HeaderValue::from_static("0"); + res.headers_mut().insert(CONTENT_LENGTH, HV_ZERO) + } BodySize::Sized(len) => { let mut buf = itoa::Buffer::new(); diff --git a/actix-http/src/header/shared/content_encoding.rs b/actix-http/src/header/shared/content_encoding.rs index a6e52138d..68511a8ee 100644 --- a/actix-http/src/header/shared/content_encoding.rs +++ b/actix-http/src/header/shared/content_encoding.rs @@ -45,13 +45,13 @@ pub enum ContentEncoding { impl ContentEncoding { /// Is the content compressed? #[inline] - pub fn is_compression(self) -> bool { + pub const fn is_compression(self) -> bool { matches!(self, ContentEncoding::Identity | ContentEncoding::Auto) } /// Convert content encoding to string. #[inline] - pub fn as_str(self) -> &'static str { + pub const fn as_str(self) -> &'static str { match self { ContentEncoding::Br => "br", ContentEncoding::Gzip => "gzip", diff --git a/actix-http/src/ws/mod.rs b/actix-http/src/ws/mod.rs index 6491da149..568d801a2 100644 --- a/actix-http/src/ws/mod.rs +++ b/actix-http/src/ws/mod.rs @@ -99,8 +99,9 @@ impl From for Response { match err { HandshakeError::GetMethodRequired => { let mut res = Response::new(StatusCode::METHOD_NOT_ALLOWED); - res.headers_mut() - .insert(header::ALLOW, HeaderValue::from_static("GET")); + #[allow(clippy::declare_interior_mutable_const)] + const HV_GET: HeaderValue = HeaderValue::from_static("GET"); + res.headers_mut().insert(header::ALLOW, HV_GET); res } diff --git a/awc/src/client/h2proto.rs b/awc/src/client/h2proto.rs index 9ced5776b..709896ddd 100644 --- a/awc/src/client/h2proto.rs +++ b/awc/src/client/h2proto.rs @@ -52,9 +52,11 @@ where let _ = match length { BodySize::None => None, - BodySize::Sized(0) => req - .headers_mut() - .insert(CONTENT_LENGTH, HeaderValue::from_static("0")), + BodySize::Sized(0) => { + #[allow(clippy::declare_interior_mutable_const)] + const HV_ZERO: HeaderValue = HeaderValue::from_static("0"); + req.headers_mut().insert(CONTENT_LENGTH, HV_ZERO) + } BodySize::Sized(len) => { let mut buf = itoa::Buffer::new(); diff --git a/awc/src/ws.rs b/awc/src/ws.rs index 96f8cf893..f3ee02d43 100644 --- a/awc/src/ws.rs +++ b/awc/src/ws.rs @@ -300,13 +300,16 @@ impl WebsocketsRequest { } self.head.set_connection_type(ConnectionType::Upgrade); + + #[allow(clippy::declare_interior_mutable_const)] + const HV_WEBSOCKET: HeaderValue = HeaderValue::from_static("websocket"); + self.head.headers.insert(header::UPGRADE, HV_WEBSOCKET); + + #[allow(clippy::declare_interior_mutable_const)] + const HV_THIRTEEN: HeaderValue = HeaderValue::from_static("13"); self.head .headers - .insert(header::UPGRADE, HeaderValue::from_static("websocket")); - self.head.headers.insert( - header::SEC_WEBSOCKET_VERSION, - HeaderValue::from_static("13"), - ); + .insert(header::SEC_WEBSOCKET_VERSION, HV_THIRTEEN); if let Some(protocols) = self.protocols.take() { self.head.headers.insert( diff --git a/src/middleware/default_headers.rs b/src/middleware/default_headers.rs index 89210b156..003abd40d 100644 --- a/src/middleware/default_headers.rs +++ b/src/middleware/default_headers.rs @@ -100,10 +100,9 @@ impl DefaultHeaders { /// /// Default is `application/octet-stream`. pub fn add_content_type(self) -> Self { - self.add(( - CONTENT_TYPE, - HeaderValue::from_static("application/octet-stream"), - )) + #[allow(clippy::declare_interior_mutable_const)] + const HV_MIME: HeaderValue = HeaderValue::from_static("application/octet-stream"); + self.add((CONTENT_TYPE, HV_MIME)) } } From 76684a786ed82a4e39acb81eedf798de942e4b61 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 27 Dec 2021 18:45:31 +0000 Subject: [PATCH 79/87] update server dep to rc2 (#2550) --- CHANGES.md | 3 +++ Cargo.toml | 10 +++++----- README.md | 4 ++-- actix-files/CHANGES.md | 4 ++++ actix-files/Cargo.toml | 10 +++++----- actix-files/README.md | 4 ++-- actix-http-test/CHANGES.md | 6 ++++++ actix-http-test/Cargo.toml | 10 +++++----- actix-http-test/README.md | 4 ++-- actix-http-test/src/lib.rs | 6 +++--- actix-http/CHANGES.md | 3 +++ actix-http/Cargo.toml | 8 ++++---- actix-http/README.md | 4 ++-- actix-multipart/CHANGES.md | 4 ++++ actix-multipart/Cargo.toml | 6 +++--- actix-multipart/README.md | 4 ++-- actix-test/CHANGES.md | 4 ++++ actix-test/Cargo.toml | 10 +++++----- actix-web-actors/CHANGES.md | 4 ++++ actix-web-actors/Cargo.toml | 10 +++++----- actix-web-actors/README.md | 4 ++-- actix-web-codegen/Cargo.toml | 4 ++-- awc/CHANGES.md | 3 +++ awc/Cargo.toml | 14 +++++++------- awc/README.md | 4 ++-- 25 files changed, 89 insertions(+), 58 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index a43b3ee41..e1317a7a5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 4.0.0-beta.16 - 2021-12-27 ### Changed - No longer require `Scope` service body type to be boxed. [#2523] - No longer require `Resource` service body type to be boxed. [#2526] diff --git a/Cargo.toml b/Cargo.toml index 7b095af91..b6ef184e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "4.0.0-beta.15" +version = "4.0.0-beta.16" authors = ["Nikolay Kim "] description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust" keywords = ["actix", "http", "web", "framework", "async"] @@ -72,12 +72,12 @@ experimental-io-uring = ["actix-server/io-uring"] actix-codec = "0.4.1" actix-macros = "0.2.3" actix-rt = "2.3" -actix-server = "2.0.0-rc.1" +actix-server = "2.0.0-rc.2" actix-service = "2.0.0" actix-utils = "3.0.0" actix-tls = { version = "3.0.0", default-features = false, optional = true } -actix-http = "3.0.0-beta.16" +actix-http = "3.0.0-beta.17" actix-router = "0.5.0-beta.3" actix-web-codegen = "0.5.0-beta.6" @@ -106,8 +106,8 @@ time = { version = "0.3", default-features = false, features = ["formatting"] } url = "2.1" [dev-dependencies] -actix-test = { version = "0.1.0-beta.9", features = ["openssl", "rustls"] } -awc = { version = "3.0.0-beta.14", features = ["openssl"] } +actix-test = { version = "0.1.0-beta.10", features = ["openssl", "rustls"] } +awc = { version = "3.0.0-beta.15", features = ["openssl"] } brotli2 = "0.3.2" criterion = { version = "0.3", features = ["html_reports"] } diff --git a/README.md b/README.md index a9afbf386..f9d388f8b 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@

[![crates.io](https://img.shields.io/crates/v/actix-web?label=latest)](https://crates.io/crates/actix-web) -[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.0.0-beta.15)](https://docs.rs/actix-web/4.0.0-beta.15) +[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.0.0-beta.16)](https://docs.rs/actix-web/4.0.0-beta.16) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg) -[![Dependency Status](https://deps.rs/crate/actix-web/4.0.0-beta.15/status.svg)](https://deps.rs/crate/actix-web/4.0.0-beta.15) +[![Dependency Status](https://deps.rs/crate/actix-web/4.0.0-beta.16/status.svg)](https://deps.rs/crate/actix-web/4.0.0-beta.16)
[![build status](https://github.com/actix/actix-web/workflows/CI%20%28Linux%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-web/actions) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index ef8eba0fc..af6dcb415 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 0.6.0-beta.11 - 2021-12-27 +* No significant changes since `0.6.0-beta.10`. + + ## 0.6.0-beta.10 - 2021-12-11 - No significant changes since `0.6.0-beta.9`. diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index c74a8e9a6..bbd4fee22 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.6.0-beta.10" +version = "0.6.0-beta.11" authors = [ "Nikolay Kim ", "fakeshadow <24548779@qq.com>", @@ -22,10 +22,10 @@ path = "src/lib.rs" experimental-io-uring = ["actix-web/experimental-io-uring", "tokio-uring"] [dependencies] -actix-http = "3.0.0-beta.16" +actix-http = "3.0.0-beta.17" actix-service = "2" actix-utils = "3" -actix-web = { version = "4.0.0-beta.15", default-features = false } +actix-web = { version = "4.0.0-beta.16", default-features = false } askama_escape = "0.10" bitflags = "1" @@ -43,5 +43,5 @@ tokio-uring = { version = "0.1", optional = true } [dev-dependencies] actix-rt = "2.2" -actix-test = "0.1.0-beta.9" -actix-web = "4.0.0-beta.15" +actix-test = "0.1.0-beta.10" +actix-web = "4.0.0-beta.16" diff --git a/actix-files/README.md b/actix-files/README.md index d686e255c..db5c94d1e 100644 --- a/actix-files/README.md +++ b/actix-files/README.md @@ -3,11 +3,11 @@ > Static file serving for Actix Web [![crates.io](https://img.shields.io/crates/v/actix-files?label=latest)](https://crates.io/crates/actix-files) -[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.0-beta.10)](https://docs.rs/actix-files/0.6.0-beta.10) +[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.0-beta.11)](https://docs.rs/actix-files/0.6.0-beta.11) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![License](https://img.shields.io/crates/l/actix-files.svg)
-[![dependency status](https://deps.rs/crate/actix-files/0.6.0-beta.10/status.svg)](https://deps.rs/crate/actix-files/0.6.0-beta.10) +[![dependency status](https://deps.rs/crate/actix-files/0.6.0-beta.11/status.svg)](https://deps.rs/crate/actix-files/0.6.0-beta.11) [![Download](https://img.shields.io/crates/d/actix-files.svg)](https://crates.io/crates/actix-files) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-http-test/CHANGES.md b/actix-http-test/CHANGES.md index 4e86e20e8..8c6a63b72 100644 --- a/actix-http-test/CHANGES.md +++ b/actix-http-test/CHANGES.md @@ -3,6 +3,12 @@ ## Unreleased - 2021-xx-xx +## 3.0.0-beta.10 - 2021-12-27 +- Update `actix-server` to `2.0.0-rc.2`. [#2550] + +[#2550]: https://github.com/actix/actix-web/pull/2550 + + ## 3.0.0-beta.9 - 2021-12-11 - No significant changes since `3.0.0-beta.8`. diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index 2ad620b08..f071678a4 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http-test" -version = "3.0.0-beta.9" +version = "3.0.0-beta.10" authors = ["Nikolay Kim "] description = "Various helpers for Actix applications to use during testing" keywords = ["http", "web", "framework", "async", "futures"] @@ -34,8 +34,8 @@ actix-codec = "0.4.1" actix-tls = "3.0.0" actix-utils = "3.0.0" actix-rt = "2.2" -actix-server = "2.0.0-rc.1" -awc = { version = "3.0.0-beta.14", default-features = false } +actix-server = "2.0.0-rc.2" +awc = { version = "3.0.0-beta.15", default-features = false } base64 = "0.13" bytes = "1" @@ -51,5 +51,5 @@ tls-openssl = { version = "0.10.9", package = "openssl", optional = true } tokio = { version = "1.8", features = ["sync"] } [dev-dependencies] -actix-web = { version = "4.0.0-beta.15", default-features = false, features = ["cookies"] } -actix-http = "3.0.0-beta.16" +actix-web = { version = "4.0.0-beta.16", default-features = false, features = ["cookies"] } +actix-http = "3.0.0-beta.17" diff --git a/actix-http-test/README.md b/actix-http-test/README.md index a0a8f1cd8..589c54c23 100644 --- a/actix-http-test/README.md +++ b/actix-http-test/README.md @@ -3,11 +3,11 @@ > Various helpers for Actix applications to use during testing. [![crates.io](https://img.shields.io/crates/v/actix-http-test?label=latest)](https://crates.io/crates/actix-http-test) -[![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.0.0-beta.9)](https://docs.rs/actix-http-test/3.0.0-beta.9) +[![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.0.0-beta.10)](https://docs.rs/actix-http-test/3.0.0-beta.10) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http-test)
-[![Dependency Status](https://deps.rs/crate/actix-http-test/3.0.0-beta.9/status.svg)](https://deps.rs/crate/actix-http-test/3.0.0-beta.9) +[![Dependency Status](https://deps.rs/crate/actix-http-test/3.0.0-beta.10/status.svg)](https://deps.rs/crate/actix-http-test/3.0.0-beta.10) [![Download](https://img.shields.io/crates/d/actix-http-test.svg)](https://crates.io/crates/actix-http-test) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-http-test/src/lib.rs b/actix-http-test/src/lib.rs index 03239ece6..8636ef9c4 100644 --- a/actix-http-test/src/lib.rs +++ b/actix-http-test/src/lib.rs @@ -12,7 +12,7 @@ use std::{net, thread, time::Duration}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_rt::{net::TcpStream, System}; -use actix_server::{Server, ServiceFactory}; +use actix_server::{Server, ServerServiceFactory}; use awc::{ error::PayloadError, http::header::HeaderMap, ws, Client, ClientRequest, ClientResponse, Connector, @@ -51,13 +51,13 @@ use tokio::sync::mpsc; /// assert!(response.status().is_success()); /// } /// ``` -pub async fn test_server>(factory: F) -> TestServer { +pub async fn test_server>(factory: F) -> TestServer { let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); test_server_with_addr(tcp, factory).await } /// Start [`test server`](test_server()) on an existing address binding. -pub async fn test_server_with_addr>( +pub async fn test_server_with_addr>( tcp: net::TcpListener, factory: F, ) -> TestServer { diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index adc4c35c7..d74a754ac 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 3.0.0-beta.17 - 2021-12-27 ### Changes - `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527] - `Payload` inner fields are now named. [#2545] diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 3fa437562..b27cb4053 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "3.0.0-beta.16" +version = "3.0.0-beta.17" authors = ["Nikolay Kim "] description = "HTTP primitives for the Actix ecosystem" keywords = ["actix", "http", "framework", "async", "futures"] @@ -79,10 +79,10 @@ flate2 = { version = "1.0.13", optional = true } zstd = { version = "0.9", optional = true } [dev-dependencies] -actix-http-test = { version = "3.0.0-beta.9", features = ["openssl"] } -actix-server = "2.0.0-rc.1" +actix-http-test = { version = "3.0.0-beta.10", features = ["openssl"] } +actix-server = "2.0.0-rc.2" actix-tls = { version = "3.0.0", features = ["openssl"] } -actix-web = "4.0.0-beta.15" +actix-web = "4.0.0-beta.16" async-stream = "0.3" criterion = { version = "0.3", features = ["html_reports"] } diff --git a/actix-http/README.md b/actix-http/README.md index 05edffd2c..223e18ceb 100644 --- a/actix-http/README.md +++ b/actix-http/README.md @@ -3,11 +3,11 @@ > HTTP primitives for the Actix ecosystem. [![crates.io](https://img.shields.io/crates/v/actix-http?label=latest)](https://crates.io/crates/actix-http) -[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.0.0-beta.16)](https://docs.rs/actix-http/3.0.0-beta.16) +[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.0.0-beta.17)](https://docs.rs/actix-http/3.0.0-beta.17) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg)
-[![dependency status](https://deps.rs/crate/actix-http/3.0.0-beta.16/status.svg)](https://deps.rs/crate/actix-http/3.0.0-beta.16) +[![dependency status](https://deps.rs/crate/actix-http/3.0.0-beta.17/status.svg)](https://deps.rs/crate/actix-http/3.0.0-beta.17) [![Download](https://img.shields.io/crates/d/actix-http.svg)](https://crates.io/crates/actix-http) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-multipart/CHANGES.md b/actix-multipart/CHANGES.md index e58c3ee24..a9a1e8784 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 0.4.0-beta.11 - 2021-12-27 +* No significant changes since `0.4.0-beta.10`. + + ## 0.4.0-beta.10 - 2021-12-11 - No significant changes since `0.4.0-beta.9`. diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 595c14d7e..8338eb952 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-multipart" -version = "0.4.0-beta.10" +version = "0.4.0-beta.11" authors = ["Nikolay Kim "] description = "Multipart form support for Actix Web" keywords = ["http", "web", "framework", "async", "futures"] @@ -15,7 +15,7 @@ path = "src/lib.rs" [dependencies] actix-utils = "3.0.0" -actix-web = { version = "4.0.0-beta.15", default-features = false } +actix-web = { version = "4.0.0-beta.16", default-features = false } bytes = "1" derive_more = "0.99.5" @@ -28,7 +28,7 @@ twoway = "0.2" [dev-dependencies] actix-rt = "2.2" -actix-http = "3.0.0-beta.16" +actix-http = "3.0.0-beta.17" futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] } tokio = { version = "1.8", features = ["sync"] } tokio-stream = "0.1" diff --git a/actix-multipart/README.md b/actix-multipart/README.md index 647796557..a9ee325ba 100644 --- a/actix-multipart/README.md +++ b/actix-multipart/README.md @@ -3,11 +3,11 @@ > Multipart form support for Actix Web. [![crates.io](https://img.shields.io/crates/v/actix-multipart?label=latest)](https://crates.io/crates/actix-multipart) -[![Documentation](https://docs.rs/actix-multipart/badge.svg?version=0.4.0-beta.10)](https://docs.rs/actix-multipart/0.4.0-beta.10) +[![Documentation](https://docs.rs/actix-multipart/badge.svg?version=0.4.0-beta.11)](https://docs.rs/actix-multipart/0.4.0-beta.11) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-multipart.svg)
-[![dependency status](https://deps.rs/crate/actix-multipart/0.4.0-beta.10/status.svg)](https://deps.rs/crate/actix-multipart/0.4.0-beta.10) +[![dependency status](https://deps.rs/crate/actix-multipart/0.4.0-beta.11/status.svg)](https://deps.rs/crate/actix-multipart/0.4.0-beta.11) [![Download](https://img.shields.io/crates/d/actix-multipart.svg)](https://crates.io/crates/actix-multipart) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-test/CHANGES.md b/actix-test/CHANGES.md index e3deeb3f4..2de0a69d6 100644 --- a/actix-test/CHANGES.md +++ b/actix-test/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 0.1.0-beta.10 - 2021-12-27 +* No significant changes since `0.1.0-beta.9`. + + ## 0.1.0-beta.9 - 2021-12-17 - Re-export `actix_http::body::to_bytes`. [#2518] - Update `actix_web::test` re-exports. [#2518] diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index 4a4615820..b34d8e6ee 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-test" -version = "0.1.0-beta.9" +version = "0.1.0-beta.10" authors = [ "Nikolay Kim ", "Rob Ede ", @@ -29,13 +29,13 @@ openssl = ["tls-openssl", "actix-http/openssl", "awc/openssl"] [dependencies] actix-codec = "0.4.1" -actix-http = "3.0.0-beta.16" -actix-http-test = "3.0.0-beta.9" +actix-http = "3.0.0-beta.17" +actix-http-test = "3.0.0-beta.10" actix-rt = "2.1" actix-service = "2.0.0" actix-utils = "3.0.0" -actix-web = { version = "4.0.0-beta.15", default-features = false, features = ["cookies"] } -awc = { version = "3.0.0-beta.14", default-features = false, features = ["cookies"] } +actix-web = { version = "4.0.0-beta.16", default-features = false, features = ["cookies"] } +awc = { version = "3.0.0-beta.15", default-features = false, features = ["cookies"] } futures-core = { version = "0.3.7", default-features = false, features = ["std"] } futures-util = { version = "0.3.7", default-features = false, features = [] } diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md index 6abfe2c61..2fbbe7444 100644 --- a/actix-web-actors/CHANGES.md +++ b/actix-web-actors/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 4.0.0-beta.9 - 2021-12-27 +* No significant changes since `4.0.0-beta.8`. + + ## 4.0.0-beta.8 - 2021-12-11 - Add `ws:WsResponseBuilder` for building WebSocket session response. [#1920] - Deprecate `ws::{start_with_addr, start_with_protocols}`. [#1920] diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index d57f139f6..9dba2dfbd 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web-actors" -version = "4.0.0-beta.8" +version = "4.0.0-beta.9" authors = ["Nikolay Kim "] description = "Actix actors support for Actix Web" keywords = ["actix", "http", "web", "framework", "async"] @@ -16,8 +16,8 @@ path = "src/lib.rs" [dependencies] actix = { version = "0.12.0", default-features = false } actix-codec = "0.4.1" -actix-http = "3.0.0-beta.16" -actix-web = { version = "4.0.0-beta.15", default-features = false } +actix-http = "3.0.0-beta.17" +actix-web = { version = "4.0.0-beta.16", default-features = false } bytes = "1" bytestring = "1" @@ -27,8 +27,8 @@ tokio = { version = "1.8", features = ["sync"] } [dev-dependencies] actix-rt = "2.2" -actix-test = "0.1.0-beta.9" -awc = { version = "3.0.0-beta.14", default-features = false } +actix-test = "0.1.0-beta.10" +awc = { version = "3.0.0-beta.15", default-features = false } env_logger = "0.9" futures-util = { version = "0.3.7", default-features = false } diff --git a/actix-web-actors/README.md b/actix-web-actors/README.md index 954f8273b..232c81eac 100644 --- a/actix-web-actors/README.md +++ b/actix-web-actors/README.md @@ -3,11 +3,11 @@ > Actix actors support for Actix Web. [![crates.io](https://img.shields.io/crates/v/actix-web-actors?label=latest)](https://crates.io/crates/actix-web-actors) -[![Documentation](https://docs.rs/actix-web-actors/badge.svg?version=4.0.0-beta.8)](https://docs.rs/actix-web-actors/4.0.0-beta.8) +[![Documentation](https://docs.rs/actix-web-actors/badge.svg?version=4.0.0-beta.9)](https://docs.rs/actix-web-actors/4.0.0-beta.9) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![License](https://img.shields.io/crates/l/actix-web-actors.svg)
-[![dependency status](https://deps.rs/crate/actix-web-actors/4.0.0-beta.8/status.svg)](https://deps.rs/crate/actix-web-actors/4.0.0-beta.8) +[![dependency status](https://deps.rs/crate/actix-web-actors/4.0.0-beta.9/status.svg)](https://deps.rs/crate/actix-web-actors/4.0.0-beta.9) [![Download](https://img.shields.io/crates/d/actix-web-actors.svg)](https://crates.io/crates/actix-web-actors) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 8d42137e7..5250baa90 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -23,9 +23,9 @@ actix-router = "0.5.0-beta.3" [dev-dependencies] actix-macros = "0.2.3" actix-rt = "2.2" -actix-test = "0.1.0-beta.9" +actix-test = "0.1.0-beta.10" actix-utils = "3.0.0" -actix-web = "4.0.0-beta.15" +actix-web = "4.0.0-beta.16" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } trybuild = "1" diff --git a/awc/CHANGES.md b/awc/CHANGES.md index e1a059481..200ad846a 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 3.0.0-beta.15 - 2021-12-27 - Rename `Connector::{ssl => openssl}`. [#2503] - Improve `Client` instantiation efficiency when using `openssl` by only building connectors once. [#2503] - `ClientRequest::send_body` now takes an `impl MessageBody`. [#2546] diff --git a/awc/Cargo.toml b/awc/Cargo.toml index d0494e023..b77662de0 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "3.0.0-beta.14" +version = "3.0.0-beta.15" authors = [ "Nikolay Kim ", "fakeshadow <24548779@qq.com>", @@ -60,7 +60,7 @@ dangerous-h2c = [] [dependencies] actix-codec = "0.4.1" actix-service = "2.0.0" -actix-http = "3.0.0-beta.16" +actix-http = "3.0.0-beta.17" actix-rt = { version = "2.1", default-features = false } actix-tls = { version = "3.0.0", features = ["connect", "uri"] } actix-utils = "3.0.0" @@ -93,13 +93,13 @@ tls-rustls = { package = "rustls", version = "0.20.0", optional = true, features trust-dns-resolver = { version = "0.20.0", optional = true } [dev-dependencies] -actix-http = { version = "3.0.0-beta.16", features = ["openssl"] } -actix-http-test = { version = "3.0.0-beta.9", features = ["openssl"] } -actix-server = "2.0.0-rc.1" -actix-test = { version = "0.1.0-beta.9", features = ["openssl", "rustls"] } +actix-http = { version = "3.0.0-beta.17", features = ["openssl"] } +actix-http-test = { version = "3.0.0-beta.10", features = ["openssl"] } +actix-server = "2.0.0-rc.2" +actix-test = { version = "0.1.0-beta.10", features = ["openssl", "rustls"] } actix-tls = { version = "3.0.0", features = ["openssl", "rustls"] } actix-utils = "3.0.0" -actix-web = { version = "4.0.0-beta.15", features = ["openssl"] } +actix-web = { version = "4.0.0-beta.16", features = ["openssl"] } brotli2 = "0.3.2" env_logger = "0.9" diff --git a/awc/README.md b/awc/README.md index 3fbdd903a..582ecb18f 100644 --- a/awc/README.md +++ b/awc/README.md @@ -3,9 +3,9 @@ > Async HTTP and WebSocket client library. [![crates.io](https://img.shields.io/crates/v/awc?label=latest)](https://crates.io/crates/awc) -[![Documentation](https://docs.rs/awc/badge.svg?version=3.0.0-beta.14)](https://docs.rs/awc/3.0.0-beta.14) +[![Documentation](https://docs.rs/awc/badge.svg?version=3.0.0-beta.15)](https://docs.rs/awc/3.0.0-beta.15) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/awc) -[![Dependency Status](https://deps.rs/crate/awc/3.0.0-beta.14/status.svg)](https://deps.rs/crate/awc/3.0.0-beta.14) +[![Dependency Status](https://deps.rs/crate/awc/3.0.0-beta.15/status.svg)](https://deps.rs/crate/awc/3.0.0-beta.15) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) ## Documentation & Resources From 36193b0a50d7453fb55d0c4d72b3409fa767426e Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 27 Dec 2021 18:54:10 +0000 Subject: [PATCH 80/87] specify tokio dep to avoid RUSTSEC-2021-0124 warning --- actix-http-test/Cargo.toml | 2 +- actix-http/Cargo.toml | 2 +- actix-multipart/Cargo.toml | 2 +- actix-test/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index f071678a4..5c58978ea 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -48,7 +48,7 @@ serde_json = "1.0" slab = "0.4" serde_urlencoded = "0.7" tls-openssl = { version = "0.10.9", package = "openssl", optional = true } -tokio = { version = "1.8", features = ["sync"] } +tokio = { version = "1.8.4", features = ["sync"] } [dev-dependencies] actix-web = { version = "4.0.0-beta.16", default-features = false, features = ["cookies"] } diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index b27cb4053..9e587890b 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -96,7 +96,7 @@ serde_json = "1.0" static_assertions = "1" tls-openssl = { package = "openssl", version = "0.10.9" } tls-rustls = { package = "rustls", version = "0.20.0" } -tokio = { version = "1.8", features = ["net", "rt", "macros"] } +tokio = { version = "1.8.4", features = ["net", "rt", "macros"] } [[example]] name = "ws" diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 8338eb952..de13133a1 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -30,5 +30,5 @@ twoway = "0.2" actix-rt = "2.2" actix-http = "3.0.0-beta.17" futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] } -tokio = { version = "1.8", features = ["sync"] } +tokio = { version = "1.8.4", features = ["sync"] } tokio-stream = "0.1" diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index b34d8e6ee..c7177a38c 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -45,4 +45,4 @@ serde_json = "1" serde_urlencoded = "0.7" tls-openssl = { package = "openssl", version = "0.10.9", optional = true } tls-rustls = { package = "rustls", version = "0.20.0", optional = true } -tokio = { version = "1.8", features = ["sync"] } +tokio = { version = "1.8.4", features = ["sync"] } diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 9dba2dfbd..3b792093a 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -23,7 +23,7 @@ bytes = "1" bytestring = "1" futures-core = { version = "0.3.7", default-features = false } pin-project-lite = "0.2" -tokio = { version = "1.8", features = ["sync"] } +tokio = { version = "1.8.4", features = ["sync"] } [dev-dependencies] actix-rt = "2.2" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index b77662de0..8777ffa74 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -83,7 +83,7 @@ rand = "0.8" serde = "1.0" serde_json = "1.0" serde_urlencoded = "0.7" -tokio = { version = "1.8", features = ["sync"] } +tokio = { version = "1.8.4", features = ["sync"] } cookie = { version = "0.15", features = ["percent-encode"], optional = true } From 4616ca8ee65ca1e14e784b8b46dc1a1306b422a5 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 28 Dec 2021 02:37:13 +0000 Subject: [PATCH 81/87] rework `Guard` trait (#2552) --- CHANGES.md | 11 + actix-files/src/service.rs | 8 +- actix-http/src/message.rs | 6 +- src/app_service.rs | 13 +- src/dev.rs | 23 +- src/guard.rs | 742 +++++++++++++++++++++--------------- src/middleware/normalize.rs | 12 +- src/route.rs | 38 +- src/scope.rs | 7 +- src/service.rs | 34 +- src/test/test_request.rs | 2 +- 11 files changed, 545 insertions(+), 351 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index e1317a7a5..f925f3b94 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,17 @@ # Changes ## Unreleased - 2021-xx-xx +### Added +- `guard::GuardContext` for use with the `Guard` trait. [#2552] +- `ServiceRequest::guard_ctx` for obtaining a guard context. [#2552] + +### Changed +- `Guard` trait now receives a `&GuardContext`. [#2552] +- `guard::fn_guard` functions now receives a `&GuardContext`. [#2552] +- Some guards now return `impl Guard` and their concrete types are made private: `guard::{Header}` and all the method guards. [#2552] +- The `Not` guard is now generic over the type of guard it wraps. [#2552] + +[#2552]: https://github.com/actix/actix-web/pull/2552 ## 4.0.0-beta.16 - 2021-12-27 diff --git a/actix-files/src/service.rs b/actix-files/src/service.rs index f6e1c2e11..057dbe5a3 100644 --- a/actix-files/src/service.rs +++ b/actix-files/src/service.rs @@ -1,8 +1,8 @@ use std::{fmt, io, ops::Deref, path::PathBuf, rc::Rc}; -use actix_service::Service; use actix_web::{ - dev::{ServiceRequest, ServiceResponse}, + body::BoxBody, + dev::{Service, ServiceRequest, ServiceResponse}, error::Error, guard::Guard, http::{header, Method}, @@ -94,7 +94,7 @@ impl fmt::Debug for FilesService { } impl Service for FilesService { - type Response = ServiceResponse; + type Response = ServiceResponse; type Error = Error; type Future = LocalBoxFuture<'static, Result>; @@ -103,7 +103,7 @@ impl Service for FilesService { fn call(&self, req: ServiceRequest) -> Self::Future { let is_method_valid = if let Some(guard) = &self.guards { // execute user defined guards - (**guard).check(req.head()) + (**guard).check(&req.guard_ctx()) } else { // default behavior matches!(*req.method(), Method::HEAD | Method::GET) diff --git a/actix-http/src/message.rs b/actix-http/src/message.rs index 34213f68a..ecd08fbb3 100644 --- a/actix-http/src/message.rs +++ b/actix-http/src/message.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, ops, rc::Rc}; use bitflags::bitflags; @@ -49,7 +49,7 @@ impl Message { } } -impl std::ops::Deref for Message { +impl ops::Deref for Message { type Target = T; fn deref(&self) -> &Self::Target { @@ -57,7 +57,7 @@ impl std::ops::Deref for Message { } } -impl std::ops::DerefMut for Message { +impl ops::DerefMut for Message { fn deref_mut(&mut self) -> &mut Self::Target { Rc::get_mut(&mut self.head).expect("Multiple copies exist") } diff --git a/src/app_service.rs b/src/app_service.rs index e0d424390..56b24f0d8 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -1,14 +1,16 @@ use std::{cell::RefCell, mem, rc::Rc}; -use actix_http::{Extensions, Request}; +use actix_http::Request; use actix_router::{Path, ResourceDef, Router, Url}; use actix_service::{boxed, fn_service, Service, ServiceFactory}; use futures_core::future::LocalBoxFuture; use futures_util::future::join_all; use crate::{ + body::BoxBody, config::{AppConfig, AppService}, data::FnDataFactory, + dev::Extensions, guard::Guard, request::{HttpRequest, HttpRequestPool}, rmap::ResourceMap, @@ -297,7 +299,7 @@ pub struct AppRouting { } impl Service for AppRouting { - type Response = ServiceResponse; + type Response = ServiceResponse; type Error = Error; type Future = LocalBoxFuture<'static, Result>; @@ -306,12 +308,15 @@ impl Service for AppRouting { fn call(&self, mut req: ServiceRequest) -> Self::Future { let res = self.router.recognize_fn(&mut req, |req, guards| { if let Some(ref guards) = guards { - for f in guards { - if !f.check(req.head()) { + let guard_ctx = req.guard_ctx(); + + for guard in guards { + if !guard.check(&guard_ctx) { return false; } } } + true }); diff --git a/src/dev.rs b/src/dev.rs index 6e1970467..bb1385bde 100644 --- a/src/dev.rs +++ b/src/dev.rs @@ -3,6 +3,16 @@ //! Most users will not have to interact with the types in this module, but it is useful for those //! writing extractors, middleware, libraries, or interacting with the service API directly. +pub use actix_http::{Extensions, Payload, RequestHead, Response, ResponseHead}; +pub use actix_router::{Path, ResourceDef, ResourcePath, Url}; +pub use actix_server::{Server, ServerHandle}; +pub use actix_service::{ + always_ready, fn_factory, fn_service, forward_ready, Service, ServiceFactory, Transform, +}; + +#[cfg(feature = "__compress")] +pub use actix_http::encoding::Decoder as Decompress; + pub use crate::config::{AppConfig, AppService}; #[doc(hidden)] pub use crate::handler::Handler; @@ -14,16 +24,6 @@ pub use crate::types::form::UrlEncoded; pub use crate::types::json::JsonBody; pub use crate::types::readlines::Readlines; -pub use actix_http::{Extensions, Payload, RequestHead, Response, ResponseHead}; -pub use actix_router::{Path, ResourceDef, ResourcePath, Url}; -pub use actix_server::{Server, ServerHandle}; -pub use actix_service::{ - always_ready, fn_factory, fn_service, forward_ready, Service, ServiceFactory, Transform, -}; - -#[cfg(feature = "__compress")] -pub use actix_http::encoding::Decoder as Decompress; - use crate::http::header::ContentEncoding; use actix_router::Patterns; @@ -46,7 +46,6 @@ pub(crate) fn ensure_leading_slash(mut patterns: Patterns) -> Patterns { patterns } -struct Enc(ContentEncoding); /// Helper trait that allows to set specific encoding for response. pub trait BodyEncoding { @@ -70,6 +69,8 @@ impl BodyEncoding for actix_http::ResponseBuilder { } } +struct Enc(ContentEncoding); + impl BodyEncoding for actix_http::Response { fn get_encoding(&self) -> Option { self.extensions().get::().map(|enc| enc.0) diff --git a/src/guard.rs b/src/guard.rs index db7f06987..fb3e4f243 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -1,160 +1,248 @@ -//! Route match guards. +//! Route guards. //! -//! Guards are one of the ways how actix-web router chooses a -//! handler service. In essence it is just a function that accepts a -//! reference to a `RequestHead` instance and returns a boolean. -//! It is possible to add guards to *scopes*, *resources* -//! and *routes*. Actix provide several guards by default, like various -//! http methods, header, etc. To become a guard, type must implement `Guard` -//! trait. Simple functions could be guards as well. +//! Guards are used during routing to help select a matching service or handler using some aspect of +//! the request; though guards should not be used for path matching since it is a built-in function +//! of the Actix Web router. //! -//! Guards can not modify the request object. But it is possible -//! to store extra attributes on a request by using the `Extensions` container. -//! Extensions containers are available via the `RequestHead::extensions()` method. +//! Guards can be used on [`Scope`]s, [`Resource`]s, [`Route`]s, and other custom services. //! +//! Fundamentally, a guard is a predicate function that receives a reference to a request context +//! object and returns a boolean; true if the request _should_ be handled by the guarded service +//! or handler. This interface is defined by the [`Guard`] trait. +//! +//! Commonly-used guards are provided in this module as well as way of creating a guard from a +//! closure ([`fn_guard`]). The [`Not`], [`Any`], and [`All`] guards are noteworthy, as they can be +//! used to compose other guards in a more flexible and semantic way than calling `.guard(...)` on +//! services multiple times (which might have different combining behavior than you want). +//! +//! Guards can not modify anything about the request. However, it is possible to store extra +//! attributes in the request-local data container obtained with [`GuardContext::req_data_mut`]. +//! +//! Guards can prevent resource definitions from overlapping (resulting in some inaccessible routes) +//! where they otherwise would when only considering paths. See the virtual hosting example below. +//! +//! # Examples +//! In the following code, the `/guarded` resource has one defined route whose handler will only be +//! called if the request method is `POST` and there is a request header with name and value equal +//! to `x-guarded` and `secret`, respectively. //! ``` -//! use actix_web::{web, http, dev, guard, App, HttpResponse}; +//! use actix_web::{web, http::Method, guard, HttpResponse}; //! -//! App::new().service(web::resource("/index.html").route( +//! web::resource("/guarded").route( //! web::route() -//! .guard(guard::Post()) -//! .guard(guard::fn_guard(|head| head.method == http::Method::GET)) -//! .to(|| HttpResponse::MethodNotAllowed())) +//! .guard(guard::Any(guard::Get()).or(guard::Post())) +//! .guard(guard::Header("x-guarded", "secret")) +//! .to(|| HttpResponse::Ok()) //! ); //! ``` +//! +//! Guards can be used to set up some form of [virtual hosting] within a single app. +//! Overlapping scope prefixes are usually discouraged, but when combined with non-overlapping guard +//! definitions they become safe to use in this way. Without these host guards, only routes under +//! the first-to-be-defined scope would be accessible. You can test this locally using `127.0.0.1` +//! and `localhost` as the `Host` guards. +//! ``` +//! use actix_web::{web, http::Method, guard, App, HttpResponse}; +//! +//! App::new() +//! .service( +//! web::scope("") +//! .guard(guard::Host("www.rust-lang.org")) +//! .default_service(web::to(|| HttpResponse::Ok().body("marketing site"))), +//! ) +//! .service( +//! web::scope("") +//! .guard(guard::Host("play.rust-lang.org")) +//! .default_service(web::to(|| HttpResponse::Ok().body("playground frontend"))), +//! ); +//! ``` +//! +//! [`Scope`]: crate::Scope::guard() +//! [`Resource`]: crate::Resource::guard() +//! [`Route`]: crate::Route::guard() +//! [virtual hosting]: https://en.wikipedia.org/wiki/Virtual_hosting -#![allow(non_snake_case)] +use std::{ + cell::{Ref, RefMut}, + convert::TryFrom, + rc::Rc, +}; -use std::rc::Rc; -use std::{convert::TryFrom, ops::Deref}; +use actix_http::{header, uri::Uri, Extensions, Method as HttpMethod, RequestHead}; -use actix_http::{header, uri::Uri, Method as HttpMethod, RequestHead}; +use crate::service::ServiceRequest; -/// Trait defines resource guards. Guards are used for route selection. -/// -/// Guards can not modify the request object. But it is possible -/// to store extra attributes on a request by using the `Extensions` container. -/// Extensions containers are available via the `RequestHead::extensions()` method. -pub trait Guard { - /// Check if request matches predicate - fn check(&self, request: &RequestHead) -> bool; +/// Provides access to request parts that are useful during routing. +#[derive(Debug)] +pub struct GuardContext<'a> { + pub(crate) req: &'a ServiceRequest, } -impl Guard for Rc { - fn check(&self, request: &RequestHead) -> bool { - self.deref().check(request) +impl<'a> GuardContext<'a> { + /// Returns reference to the request head. + #[inline] + pub fn head(&self) -> &RequestHead { + self.req.head() + } + + /// Returns reference to the request-local data container. + #[inline] + pub fn req_data(&self) -> Ref<'a, Extensions> { + self.req.req_data() + } + + /// Returns mutable reference to the request-local data container. + #[inline] + pub fn req_data_mut(&self) -> RefMut<'a, Extensions> { + self.req.req_data_mut() } } -/// Create guard object for supplied function. +/// Interface for routing guards. /// +/// See [module level documentation](self) for more. +pub trait Guard { + /// Returns true if predicate condition is met for a given request. + fn check(&self, ctx: &GuardContext<'_>) -> bool; +} + +impl Guard for Rc { + fn check(&self, ctx: &GuardContext<'_>) -> bool { + (**self).check(ctx) + } +} + +/// Creates a guard using the given function. +/// +/// # Examples /// ``` -/// use actix_web::{guard, web, App, HttpResponse}; +/// use actix_web::{guard, web, HttpResponse}; /// -/// App::new().service(web::resource("/index.html").route( -/// web::route() -/// .guard( -/// guard::fn_guard( -/// |req| req.headers() -/// .contains_key("content-type"))) -/// .to(|| HttpResponse::MethodNotAllowed())) -/// ); +/// web::route() +/// .guard(guard::fn_guard(|ctx| { +/// ctx.head().headers().contains_key("content-type") +/// })) +/// .to(|| HttpResponse::Ok()); /// ``` pub fn fn_guard(f: F) -> impl Guard where - F: Fn(&RequestHead) -> bool, + F: Fn(&GuardContext<'_>) -> bool, { FnGuard(f) } -struct FnGuard bool>(F); +struct FnGuard) -> bool>(F); impl Guard for FnGuard where - F: Fn(&RequestHead) -> bool, + F: Fn(&GuardContext<'_>) -> bool, { - fn check(&self, head: &RequestHead) -> bool { - (self.0)(head) + fn check(&self, ctx: &GuardContext<'_>) -> bool { + (self.0)(ctx) } } impl Guard for F where - F: Fn(&RequestHead) -> bool, + F: Fn(&GuardContext<'_>) -> bool, { - fn check(&self, head: &RequestHead) -> bool { - (self)(head) + fn check(&self, ctx: &GuardContext<'_>) -> bool { + (self)(ctx) } } -/// Return guard that matches if any of supplied guards. +/// Creates a guard that matches if any added guards match. /// +/// # Examples +/// The handler below will be called for either request method `GET` or `POST`. /// ``` -/// use actix_web::{web, guard, App, HttpResponse}; +/// use actix_web::{web, guard, HttpResponse}; /// -/// App::new().service(web::resource("/index.html").route( -/// web::route() -/// .guard(guard::Any(guard::Get()).or(guard::Post())) -/// .to(|| HttpResponse::MethodNotAllowed())) -/// ); +/// web::route() +/// .guard( +/// guard::Any(guard::Get()) +/// .or(guard::Post())) +/// .to(|| HttpResponse::Ok()); /// ``` +#[allow(non_snake_case)] pub fn Any(guard: F) -> AnyGuard { - AnyGuard(vec![Box::new(guard)]) + AnyGuard { + guards: vec![Box::new(guard)], + } } -/// Matches any of supplied guards. -pub struct AnyGuard(Vec>); +/// A collection of guards that match if the disjunction of their `check` outcomes is true. +/// +/// That is, only one contained guard needs to match in order for the aggregate guard to match. +/// +/// Construct an `AnyGuard` using [`Any`]. +pub struct AnyGuard { + guards: Vec>, +} impl AnyGuard { - /// Add guard to a list of guards to check + /// Adds new guard to the collection of guards to check. pub fn or(mut self, guard: F) -> Self { - self.0.push(Box::new(guard)); + self.guards.push(Box::new(guard)); self } } impl Guard for AnyGuard { - fn check(&self, req: &RequestHead) -> bool { - for p in &self.0 { - if p.check(req) { + fn check(&self, ctx: &GuardContext<'_>) -> bool { + for guard in &self.guards { + if guard.check(ctx) { return true; } } + false } } -/// Return guard that matches if all of the supplied guards. +/// Creates a guard that matches if all added guards match. /// +/// # Examples +/// The handler below will only be called if the request method is `GET` **and** the specified +/// header name and value match exactly. /// ``` -/// use actix_web::{guard, web, App, HttpResponse}; +/// use actix_web::{guard, web, HttpResponse}; /// -/// App::new().service(web::resource("/index.html").route( -/// web::route() -/// .guard( -/// guard::All(guard::Get()).and(guard::Header("content-type", "text/plain"))) -/// .to(|| HttpResponse::MethodNotAllowed())) -/// ); +/// web::route() +/// .guard( +/// guard::All(guard::Get()) +/// .and(guard::Header("accept", "text/plain")) +/// ) +/// .to(|| HttpResponse::Ok()); /// ``` +#[allow(non_snake_case)] pub fn All(guard: F) -> AllGuard { - AllGuard(vec![Box::new(guard)]) + AllGuard { + guards: vec![Box::new(guard)], + } } -/// Matches if all of supplied guards. -pub struct AllGuard(Vec>); +/// A collection of guards that match if the conjunction of their `check` outcomes is true. +/// +/// That is, **all** contained guard needs to match in order for the aggregate guard to match. +/// +/// Construct an `AllGuard` using [`All`]. +pub struct AllGuard { + guards: Vec>, +} impl AllGuard { - /// Add new guard to the list of guards to check + /// Adds new guard to the collection of guards to check. pub fn and(mut self, guard: F) -> Self { - self.0.push(Box::new(guard)); + self.guards.push(Box::new(guard)); self } } impl Guard for AllGuard { - fn check(&self, request: &RequestHead) -> bool { - for p in &self.0 { - if !p.check(request) { + fn check(&self, ctx: &GuardContext<'_>) -> bool { + for guard in &self.guards { + if !guard.check(ctx) { return false; } } @@ -162,159 +250,189 @@ impl Guard for AllGuard { } } -/// Return guard that matches if supplied guard does not match. -pub fn Not(guard: F) -> NotGuard { - NotGuard(Box::new(guard)) -} +/// Wraps a guard and inverts the outcome of it's `Guard` implementation. +/// +/// # Examples +/// The handler below will be called for any request method apart from `GET`. +/// ``` +/// use actix_web::{guard, web, HttpResponse}; +/// +/// web::route() +/// .guard(guard::Not(guard::Get())) +/// .to(|| HttpResponse::Ok()); +/// ``` +pub struct Not(pub G); -#[doc(hidden)] -pub struct NotGuard(Box); - -impl Guard for NotGuard { - fn check(&self, request: &RequestHead) -> bool { - !self.0.check(request) +impl Guard for Not { + fn check(&self, ctx: &GuardContext<'_>) -> bool { + !self.0.check(ctx) } } -/// HTTP method guard. -#[doc(hidden)] -pub struct MethodGuard(HttpMethod); - -impl Guard for MethodGuard { - fn check(&self, request: &RequestHead) -> bool { - request.method == self.0 - } -} - -/// Guard to match *GET* HTTP method. -pub fn Get() -> MethodGuard { - MethodGuard(HttpMethod::GET) -} - -/// Predicate to match *POST* HTTP method. -pub fn Post() -> MethodGuard { - MethodGuard(HttpMethod::POST) -} - -/// Predicate to match *PUT* HTTP method. -pub fn Put() -> MethodGuard { - MethodGuard(HttpMethod::PUT) -} - -/// Predicate to match *DELETE* HTTP method. -pub fn Delete() -> MethodGuard { - MethodGuard(HttpMethod::DELETE) -} - -/// Predicate to match *HEAD* HTTP method. -pub fn Head() -> MethodGuard { - MethodGuard(HttpMethod::HEAD) -} - -/// Predicate to match *OPTIONS* HTTP method. -pub fn Options() -> MethodGuard { - MethodGuard(HttpMethod::OPTIONS) -} - -/// Predicate to match *CONNECT* HTTP method. -pub fn Connect() -> MethodGuard { - MethodGuard(HttpMethod::CONNECT) -} - -/// Predicate to match *PATCH* HTTP method. -pub fn Patch() -> MethodGuard { - MethodGuard(HttpMethod::PATCH) -} - -/// Predicate to match *TRACE* HTTP method. -pub fn Trace() -> MethodGuard { - MethodGuard(HttpMethod::TRACE) -} - -/// Predicate to match specified HTTP method. -pub fn Method(method: HttpMethod) -> MethodGuard { +/// Creates a guard that matches a specified HTTP method. +#[allow(non_snake_case)] +pub fn Method(method: HttpMethod) -> impl Guard { MethodGuard(method) } -/// Return predicate that matches if request contains specified header and -/// value. -pub fn Header(name: &'static str, value: &'static str) -> HeaderGuard { +/// HTTP method guard. +struct MethodGuard(HttpMethod); + +impl Guard for MethodGuard { + fn check(&self, ctx: &GuardContext<'_>) -> bool { + ctx.head().method == self.0 + } +} + +macro_rules! method_guard { + ($method_fn:ident, $method_const:ident) => { + paste::paste! { + #[doc = " Creates a guard that matches the `" $method_const "` request method."] + /// + /// # Examples + #[doc = " The route in this example will only respond to `" $method_const "` requests."] + /// ``` + /// use actix_web::{guard, web, HttpResponse}; + /// + /// web::route() + #[doc = " .guard(guard::" $method_fn "())"] + /// .to(|| HttpResponse::Ok()); + /// ``` + #[allow(non_snake_case)] + pub fn $method_fn() -> impl Guard { + MethodGuard(HttpMethod::$method_const) + } + } + }; +} + +method_guard!(Get, GET); +method_guard!(Post, POST); +method_guard!(Put, PUT); +method_guard!(Delete, DELETE); +method_guard!(Head, HEAD); +method_guard!(Options, OPTIONS); +method_guard!(Connect, CONNECT); +method_guard!(Patch, PATCH); +method_guard!(Trace, TRACE); + +/// Creates a guard that matches if request contains given header name and value. +/// +/// # Examples +/// The handler below will be called when the request contains an `x-guarded` header with value +/// equal to `secret`. +/// ``` +/// use actix_web::{guard, web, HttpResponse}; +/// +/// web::route() +/// .guard(guard::Header("x-guarded", "secret")) +/// .to(|| HttpResponse::Ok()); +/// ``` +#[allow(non_snake_case)] +pub fn Header(name: &'static str, value: &'static str) -> impl Guard { HeaderGuard( header::HeaderName::try_from(name).unwrap(), header::HeaderValue::from_static(value), ) } -#[doc(hidden)] -pub struct HeaderGuard(header::HeaderName, header::HeaderValue); +struct HeaderGuard(header::HeaderName, header::HeaderValue); impl Guard for HeaderGuard { - fn check(&self, req: &RequestHead) -> bool { - if let Some(val) = req.headers.get(&self.0) { + fn check(&self, ctx: &GuardContext<'_>) -> bool { + if let Some(val) = ctx.head().headers.get(&self.0) { return val == self.1; } + false } } -/// Return predicate that matches if request contains specified Host name. +/// Creates a guard that matches requests targetting a specific host. /// -/// ``` -/// use actix_web::{web, guard::Host, App, HttpResponse}; +/// # Matching Host +/// This guard will: +/// - match against the `Host` header, if present; +/// - fall-back to matching against the request target's host, if present; +/// - return false if host cannot be determined; /// -/// App::new().service( -/// web::resource("/index.html") -/// .guard(Host("www.rust-lang.org")) -/// .to(|| HttpResponse::MethodNotAllowed()) -/// ); +/// # Matching Scheme +/// Optionally, this guard can match against the host's scheme. Set the scheme for matching using +/// `Host(host).scheme(protocol)`. If the request's scheme cannot be determined, it will not prevent +/// the guard from matching successfully. +/// +/// # Examples +/// The [module-level documentation](self) has an example of virtual hosting using `Host` guards. +/// +/// The example below additionally guards on the host URI's scheme. This could allow routing to +/// different handlers for `http:` vs `https:` visitors; to redirect, for example. /// ``` -pub fn Host>(host: H) -> HostGuard { - HostGuard(host.as_ref().to_string(), None) +/// use actix_web::{web, guard::Host, HttpResponse}; +/// +/// web::scope("/admin") +/// .guard(Host("admin.rust-lang.org").scheme("https")) +/// .default_service(web::to(|| HttpResponse::Ok().body("admin connection is secure"))); +/// ``` +#[allow(non_snake_case)] +pub fn Host(host: impl AsRef) -> HostGuard { + HostGuard { + host: host.as_ref().to_string(), + scheme: None, + } } fn get_host_uri(req: &RequestHead) -> Option { - use core::str::FromStr; req.headers .get(header::HOST) .and_then(|host_value| host_value.to_str().ok()) .or_else(|| req.uri.host()) - .map(|host: &str| Uri::from_str(host).ok()) - .and_then(|host_success| host_success) + .and_then(|host| host.parse().ok()) } #[doc(hidden)] -pub struct HostGuard(String, Option); +pub struct HostGuard { + host: String, + scheme: Option, +} impl HostGuard { /// Set request scheme to match pub fn scheme>(mut self, scheme: H) -> HostGuard { - self.1 = Some(scheme.as_ref().to_string()); + self.scheme = Some(scheme.as_ref().to_string()); self } } impl Guard for HostGuard { - fn check(&self, req: &RequestHead) -> bool { - let req_host_uri = if let Some(uri) = get_host_uri(req) { - uri - } else { - return false; + fn check(&self, ctx: &GuardContext<'_>) -> bool { + // parse host URI from header or request target + let req_host_uri = match get_host_uri(ctx.head()) { + Some(uri) => uri, + + // no match if host cannot be determined + None => return false, }; - if let Some(uri_host) = req_host_uri.host() { - if self.0 != uri_host { - return false; - } - } else { - return false; + match req_host_uri.host() { + // fall through to scheme checks + Some(uri_host) if self.host == uri_host => {} + + // Either: + // - request's host does not match guard's host; + // - It was possible that the parsed URI from request target did not contain a host. + _ => return false, } - if let Some(ref scheme) = self.1 { + if let Some(ref scheme) = self.scheme { if let Some(ref req_host_uri_scheme) = req_host_uri.scheme_str() { return scheme == req_host_uri_scheme; } + + // TODO: is the the correct behavior? + // falls through if scheme cannot be determined } + // all conditions passed true } } @@ -327,171 +445,201 @@ mod tests { use crate::test::TestRequest; #[test] - fn test_header() { + fn header_match() { let req = TestRequest::default() .insert_header((header::TRANSFER_ENCODING, "chunked")) - .to_http_request(); + .to_srv_request(); - let pred = Header("transfer-encoding", "chunked"); - assert!(pred.check(req.head())); + let hdr = Header("transfer-encoding", "chunked"); + assert!(hdr.check(&req.guard_ctx())); - let pred = Header("transfer-encoding", "other"); - assert!(!pred.check(req.head())); + let hdr = Header("transfer-encoding", "other"); + assert!(!hdr.check(&req.guard_ctx())); - let pred = Header("content-type", "other"); - assert!(!pred.check(req.head())); + let hdr = Header("content-type", "chunked"); + assert!(!hdr.check(&req.guard_ctx())); + + let hdr = Header("content-type", "other"); + assert!(!hdr.check(&req.guard_ctx())); } #[test] - fn test_host() { + fn host_from_header() { let req = TestRequest::default() .insert_header(( header::HOST, header::HeaderValue::from_static("www.rust-lang.org"), )) - .to_http_request(); + .to_srv_request(); - let pred = Host("www.rust-lang.org"); - assert!(pred.check(req.head())); + let host = Host("www.rust-lang.org"); + assert!(host.check(&req.guard_ctx())); - let pred = Host("www.rust-lang.org").scheme("https"); - assert!(pred.check(req.head())); + let host = Host("www.rust-lang.org").scheme("https"); + assert!(host.check(&req.guard_ctx())); - let pred = Host("blog.rust-lang.org"); - assert!(!pred.check(req.head())); + let host = Host("blog.rust-lang.org"); + assert!(!host.check(&req.guard_ctx())); - let pred = Host("blog.rust-lang.org").scheme("https"); - assert!(!pred.check(req.head())); + let host = Host("blog.rust-lang.org").scheme("https"); + assert!(!host.check(&req.guard_ctx())); - let pred = Host("crates.io"); - assert!(!pred.check(req.head())); + let host = Host("crates.io"); + assert!(!host.check(&req.guard_ctx())); - let pred = Host("localhost"); - assert!(!pred.check(req.head())); + let host = Host("localhost"); + assert!(!host.check(&req.guard_ctx())); } #[test] - fn test_host_scheme() { + fn host_without_header() { + let req = TestRequest::default() + .uri("www.rust-lang.org") + .to_srv_request(); + + let host = Host("www.rust-lang.org"); + assert!(host.check(&req.guard_ctx())); + + let host = Host("www.rust-lang.org").scheme("https"); + assert!(host.check(&req.guard_ctx())); + + let host = Host("blog.rust-lang.org"); + assert!(!host.check(&req.guard_ctx())); + + let host = Host("blog.rust-lang.org").scheme("https"); + assert!(!host.check(&req.guard_ctx())); + + let host = Host("crates.io"); + assert!(!host.check(&req.guard_ctx())); + + let host = Host("localhost"); + assert!(!host.check(&req.guard_ctx())); + } + + #[test] + fn host_scheme() { let req = TestRequest::default() .insert_header(( header::HOST, header::HeaderValue::from_static("https://www.rust-lang.org"), )) - .to_http_request(); + .to_srv_request(); - let pred = Host("www.rust-lang.org").scheme("https"); - assert!(pred.check(req.head())); + let host = Host("www.rust-lang.org").scheme("https"); + assert!(host.check(&req.guard_ctx())); - let pred = Host("www.rust-lang.org"); - assert!(pred.check(req.head())); + let host = Host("www.rust-lang.org"); + assert!(host.check(&req.guard_ctx())); - let pred = Host("www.rust-lang.org").scheme("http"); - assert!(!pred.check(req.head())); + let host = Host("www.rust-lang.org").scheme("http"); + assert!(!host.check(&req.guard_ctx())); - let pred = Host("blog.rust-lang.org"); - assert!(!pred.check(req.head())); + let host = Host("blog.rust-lang.org"); + assert!(!host.check(&req.guard_ctx())); - let pred = Host("blog.rust-lang.org").scheme("https"); - assert!(!pred.check(req.head())); + let host = Host("blog.rust-lang.org").scheme("https"); + assert!(!host.check(&req.guard_ctx())); - let pred = Host("crates.io").scheme("https"); - assert!(!pred.check(req.head())); + let host = Host("crates.io").scheme("https"); + assert!(!host.check(&req.guard_ctx())); - let pred = Host("localhost"); - assert!(!pred.check(req.head())); + let host = Host("localhost"); + assert!(!host.check(&req.guard_ctx())); } #[test] - fn test_host_without_header() { + fn method_guards() { + let get_req = TestRequest::get().to_srv_request(); + let post_req = TestRequest::post().to_srv_request(); + + assert!(Get().check(&get_req.guard_ctx())); + assert!(!Get().check(&post_req.guard_ctx())); + + assert!(Post().check(&post_req.guard_ctx())); + assert!(!Post().check(&get_req.guard_ctx())); + + let req = TestRequest::put().to_srv_request(); + assert!(Put().check(&req.guard_ctx())); + assert!(!Put().check(&get_req.guard_ctx())); + + let req = TestRequest::patch().to_srv_request(); + assert!(Patch().check(&req.guard_ctx())); + assert!(!Patch().check(&get_req.guard_ctx())); + + let r = TestRequest::delete().to_srv_request(); + assert!(Delete().check(&r.guard_ctx())); + assert!(!Delete().check(&get_req.guard_ctx())); + + let req = TestRequest::default().method(Method::HEAD).to_srv_request(); + assert!(Head().check(&req.guard_ctx())); + assert!(!Head().check(&get_req.guard_ctx())); + let req = TestRequest::default() - .uri("www.rust-lang.org") - .to_http_request(); - - let pred = Host("www.rust-lang.org"); - assert!(pred.check(req.head())); - - let pred = Host("www.rust-lang.org").scheme("https"); - assert!(pred.check(req.head())); - - let pred = Host("blog.rust-lang.org"); - assert!(!pred.check(req.head())); - - let pred = Host("blog.rust-lang.org").scheme("https"); - assert!(!pred.check(req.head())); - - let pred = Host("crates.io"); - assert!(!pred.check(req.head())); - - let pred = Host("localhost"); - assert!(!pred.check(req.head())); - } - - #[test] - fn test_methods() { - let req = TestRequest::default().to_http_request(); - let req2 = TestRequest::default() - .method(Method::POST) - .to_http_request(); - - assert!(Get().check(req.head())); - assert!(!Get().check(req2.head())); - assert!(Post().check(req2.head())); - assert!(!Post().check(req.head())); - - let r = TestRequest::default().method(Method::PUT).to_http_request(); - assert!(Put().check(r.head())); - assert!(!Put().check(req.head())); - - let r = TestRequest::default() - .method(Method::DELETE) - .to_http_request(); - assert!(Delete().check(r.head())); - assert!(!Delete().check(req.head())); - - let r = TestRequest::default() - .method(Method::HEAD) - .to_http_request(); - assert!(Head().check(r.head())); - assert!(!Head().check(req.head())); - - let r = TestRequest::default() .method(Method::OPTIONS) - .to_http_request(); - assert!(Options().check(r.head())); - assert!(!Options().check(req.head())); + .to_srv_request(); + assert!(Options().check(&req.guard_ctx())); + assert!(!Options().check(&get_req.guard_ctx())); - let r = TestRequest::default() + let req = TestRequest::default() .method(Method::CONNECT) - .to_http_request(); - assert!(Connect().check(r.head())); - assert!(!Connect().check(req.head())); + .to_srv_request(); + assert!(Connect().check(&req.guard_ctx())); + assert!(!Connect().check(&get_req.guard_ctx())); - let r = TestRequest::default() - .method(Method::PATCH) - .to_http_request(); - assert!(Patch().check(r.head())); - assert!(!Patch().check(req.head())); - - let r = TestRequest::default() + let req = TestRequest::default() .method(Method::TRACE) - .to_http_request(); - assert!(Trace().check(r.head())); - assert!(!Trace().check(req.head())); + .to_srv_request(); + assert!(Trace().check(&req.guard_ctx())); + assert!(!Trace().check(&get_req.guard_ctx())); } #[test] - fn test_preds() { - let r = TestRequest::default() + fn aggregate_any() { + let req = TestRequest::default() .method(Method::TRACE) - .to_http_request(); + .to_srv_request(); - assert!(Not(Get()).check(r.head())); - assert!(!Not(Trace()).check(r.head())); + assert!(Any(Trace()).check(&req.guard_ctx())); + assert!(Any(Trace()).or(Get()).check(&req.guard_ctx())); + assert!(!Any(Get()).or(Get()).check(&req.guard_ctx())); + } - assert!(All(Trace()).and(Trace()).check(r.head())); - assert!(!All(Get()).and(Trace()).check(r.head())); + #[test] + fn aggregate_all() { + let req = TestRequest::default() + .method(Method::TRACE) + .to_srv_request(); - assert!(Any(Get()).or(Trace()).check(r.head())); - assert!(!Any(Get()).or(Get()).check(r.head())); + assert!(All(Trace()).check(&req.guard_ctx())); + assert!(All(Trace()).and(Trace()).check(&req.guard_ctx())); + assert!(!All(Trace()).and(Get()).check(&req.guard_ctx())); + } + + #[test] + fn nested_not() { + let req = TestRequest::default().to_srv_request(); + + let get = Get(); + assert!(get.check(&req.guard_ctx())); + + let not_get = Not(get); + assert!(!not_get.check(&req.guard_ctx())); + + let not_not_get = Not(not_get); + assert!(not_not_get.check(&req.guard_ctx())); + } + + #[test] + fn function_guard() { + let domain = "rust-lang.org".to_owned(); + let guard = fn_guard(|ctx| ctx.head().uri.host().unwrap().ends_with(&domain)); + + let req = TestRequest::default() + .uri("blog.rust-lang.org") + .to_srv_request(); + assert!(guard.check(&req.guard_ctx())); + + let req = TestRequest::default().uri("crates.io").to_srv_request(); + assert!(!guard.check(&req.guard_ctx())); } } diff --git a/src/middleware/normalize.rs b/src/middleware/normalize.rs index 18dcaeefa..3ab908481 100644 --- a/src/middleware/normalize.rs +++ b/src/middleware/normalize.rs @@ -225,7 +225,7 @@ mod tests { .service(web::resource("/v1/something").to(HttpResponse::Ok)) .service( web::resource("/v2/something") - .guard(fn_guard(|req| req.uri.query() == Some("query=test"))) + .guard(fn_guard(|ctx| ctx.head().uri.query() == Some("query=test"))) .to(HttpResponse::Ok), ), ) @@ -261,7 +261,7 @@ mod tests { .service(web::resource("/v1/something").to(HttpResponse::Ok)) .service( web::resource("/v2/something") - .guard(fn_guard(|req| req.uri.query() == Some("query=test"))) + .guard(fn_guard(|ctx| ctx.head().uri.query() == Some("query=test"))) .to(HttpResponse::Ok), ), ) @@ -294,7 +294,7 @@ mod tests { let app = init_service( App::new().wrap(NormalizePath(TrailingSlash::Trim)).service( web::resource("/") - .guard(fn_guard(|req| req.uri.query() == Some("query=test"))) + .guard(fn_guard(|ctx| ctx.head().uri.query() == Some("query=test"))) .to(HttpResponse::Ok), ), ) @@ -318,7 +318,7 @@ mod tests { .service(web::resource("/v1/something/").to(HttpResponse::Ok)) .service( web::resource("/v2/something/") - .guard(fn_guard(|req| req.uri.query() == Some("query=test"))) + .guard(fn_guard(|ctx| ctx.head().uri.query() == Some("query=test"))) .to(HttpResponse::Ok), ), ) @@ -353,7 +353,7 @@ mod tests { .wrap(NormalizePath(TrailingSlash::Always)) .service( web::resource("/") - .guard(fn_guard(|req| req.uri.query() == Some("query=test"))) + .guard(fn_guard(|ctx| ctx.head().uri.query() == Some("query=test"))) .to(HttpResponse::Ok), ), ) @@ -378,7 +378,7 @@ mod tests { .service(web::resource("/v1/").to(HttpResponse::Ok)) .service( web::resource("/v2/something") - .guard(fn_guard(|req| req.uri.query() == Some("query=test"))) + .guard(fn_guard(|ctx| ctx.head().uri.query() == Some("query=test"))) .to(HttpResponse::Ok), ), ) diff --git a/src/route.rs b/src/route.rs index 6d6fca4b7..0410b99dd 100644 --- a/src/route.rs +++ b/src/route.rs @@ -65,9 +65,12 @@ pub struct RouteService { } impl RouteService { + // TODO: does this need to take &mut ? pub fn check(&self, req: &mut ServiceRequest) -> bool { - for f in self.guards.iter() { - if !f.check(req.head()) { + let guard_ctx = req.guard_ctx(); + + for guard in self.guards.iter() { + if !guard.check(&guard_ctx) { return false; } } @@ -90,6 +93,7 @@ impl Service for RouteService { impl Route { /// Add method guard to the route. /// + /// # Examples /// ``` /// # use actix_web::*; /// # fn main() { @@ -110,6 +114,7 @@ impl Route { /// Add guard to the route. /// + /// # Examples /// ``` /// # use actix_web::*; /// # fn main() { @@ -143,16 +148,13 @@ impl Route { /// format!("Welcome {}!", info.username) /// } /// - /// fn main() { - /// let app = App::new().service( - /// web::resource("/{username}/index.html") // <- define path parameters - /// .route(web::get().to(index)) // <- register handler - /// ); - /// } + /// let app = App::new().service( + /// web::resource("/{username}/index.html") // <- define path parameters + /// .route(web::get().to(index)) // <- register handler + /// ); /// ``` /// /// It is possible to use multiple extractors for one handler function. - /// /// ``` /// # use std::collections::HashMap; /// # use serde::Deserialize; @@ -164,16 +166,18 @@ impl Route { /// } /// /// /// extract path info using serde - /// async fn index(path: web::Path, query: web::Query>, body: web::Json) -> String { + /// async fn index( + /// path: web::Path, + /// query: web::Query>, + /// body: web::Json + /// ) -> String { /// format!("Welcome {}!", path.username) /// } /// - /// fn main() { - /// let app = App::new().service( - /// web::resource("/{username}/index.html") // <- define path parameters - /// .route(web::get().to(index)) - /// ); - /// } + /// let app = App::new().service( + /// web::resource("/{username}/index.html") // <- define path parameters + /// .route(web::get().to(index)) + /// ); /// ``` pub fn to(mut self, handler: F) -> Self where @@ -199,7 +203,7 @@ impl Route { /// type Error = Infallible; /// type Future = LocalBoxFuture<'static, Result>; /// - /// always_ready!(); + /// dev::always_ready!(); /// /// fn call(&self, req: ServiceRequest) -> Self::Future { /// let (req, _) = req.into_parts(); diff --git a/src/scope.rs b/src/scope.rs index 176e0d5a0..b4618bb6c 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -538,12 +538,15 @@ impl Service for ScopeService { fn call(&self, mut req: ServiceRequest) -> Self::Future { let res = self.router.recognize_fn(&mut req, |req, guards| { if let Some(ref guards) = guards { - for f in guards { - if !f.check(req.head()) { + let guard_ctx = req.guard_ctx(); + + for guard in guards { + if !guard.check(&guard_ctx) { return false; } } } + true }); diff --git a/src/service.rs b/src/service.rs index d5c381fa4..975556197 100644 --- a/src/service.rs +++ b/src/service.rs @@ -21,7 +21,7 @@ use cookie::{Cookie, ParseError as CookieParseError}; use crate::{ config::{AppConfig, AppService}, dev::ensure_leading_slash, - guard::Guard, + guard::{Guard, GuardContext}, info::ConnectionInfo, rmap::ResourceMap, Error, HttpRequest, HttpResponse, @@ -172,7 +172,7 @@ impl ServiceRequest { self.head().uri.path() } - /// Counterpart to [`HttpRequest::query_string`](super::HttpRequest::query_string()). + /// Counterpart to [`HttpRequest::query_string`]. #[inline] pub fn query_string(&self) -> &str { self.req.query_string() @@ -208,13 +208,13 @@ impl ServiceRequest { self.req.match_info() } - /// Counterpart to [`HttpRequest::match_name`](super::HttpRequest::match_name()). + /// Counterpart to [`HttpRequest::match_name`]. #[inline] pub fn match_name(&self) -> Option<&str> { self.req.match_name() } - /// Counterpart to [`HttpRequest::match_pattern`](super::HttpRequest::match_pattern()). + /// Counterpart to [`HttpRequest::match_pattern`]. #[inline] pub fn match_pattern(&self) -> Option { self.req.match_pattern() @@ -238,7 +238,7 @@ impl ServiceRequest { self.req.app_config() } - /// Counterpart to [`HttpRequest::app_data`](super::HttpRequest::app_data()). + /// Counterpart to [`HttpRequest::app_data`]. #[inline] pub fn app_data(&self) -> Option<&T> { for container in self.req.inner.app_data.iter().rev() { @@ -250,19 +250,33 @@ impl ServiceRequest { None } - /// Counterpart to [`HttpRequest::conn_data`](super::HttpRequest::conn_data()). + /// Counterpart to [`HttpRequest::conn_data`]. #[inline] pub fn conn_data(&self) -> Option<&T> { self.req.conn_data() } + /// Counterpart to [`HttpRequest::req_data`]. + #[inline] + pub fn req_data(&self) -> Ref<'_, Extensions> { + self.req.req_data() + } + + /// Counterpart to [`HttpRequest::req_data_mut`]. + #[inline] + pub fn req_data_mut(&self) -> RefMut<'_, Extensions> { + self.req.req_data_mut() + } + #[cfg(feature = "cookies")] + #[inline] pub fn cookies(&self) -> Result>>, CookieParseError> { self.req.cookies() } /// Return request cookie. #[cfg(feature = "cookies")] + #[inline] pub fn cookie(&self, name: &str) -> Option> { self.req.cookie(name) } @@ -283,6 +297,14 @@ impl ServiceRequest { .app_data .push(extensions); } + + /// Creates a context object for use with a [guard](crate::guard). + /// + /// Useful if you are implementing + #[inline] + pub fn guard_ctx(&self) -> GuardContext<'_> { + GuardContext { req: self } + } } impl Resource for ServiceRequest { diff --git a/src/test/test_request.rs b/src/test/test_request.rs index 5c4de9084..fc42253d7 100644 --- a/src/test/test_request.rs +++ b/src/test/test_request.rs @@ -124,7 +124,7 @@ impl TestRequest { self } - /// Set HTTP Uri of this request + /// Set HTTP URI of this request pub fn uri(mut self, path: &str) -> Self { self.req.uri(path); self From 96a4dc9decf4947b60eaf9d33f29c328265eec36 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 28 Dec 2021 03:22:22 +0000 Subject: [PATCH 82/87] use modern signatures for awc `send_*` and `header` methods (#2553) --- awc/CHANGES.md | 5 ++ awc/src/any_body.rs | 93 +--------------------------------- awc/src/frozen.rs | 58 ++++++++------------- awc/src/middleware/redirect.rs | 4 +- awc/src/request.rs | 2 +- awc/src/responses/response.rs | 7 +-- awc/src/sender.rs | 45 ++++++---------- 7 files changed, 52 insertions(+), 162 deletions(-) diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 200ad846a..06e94292a 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,6 +1,11 @@ # Changes ## Unreleased - 2021-xx-xx +- `*::send_json` and `*::send_form` methods now receive `impl Serialize`. [#2553] +- `FrozenClientRequest::extra_header` now uses receives an `impl TryIntoHeaderPair`. [#2553] +- Remove unnecessary `Unpin` bounds on `*::send_stream`. [#2553] + +[#2553]: https://github.com/actix/actix-web/pull/2553 ## 3.0.0-beta.15 - 2021-12-27 diff --git a/awc/src/any_body.rs b/awc/src/any_body.rs index 437216313..d09a943ab 100644 --- a/awc/src/any_body.rs +++ b/awc/src/any_body.rs @@ -1,17 +1,13 @@ use std::{ - borrow::Cow, fmt, mem, pin::Pin, task::{Context, Poll}, }; -use bytes::{Bytes, BytesMut}; -use futures_core::Stream; +use bytes::Bytes; use pin_project_lite::pin_project; -use actix_http::body::{BodySize, BodyStream, BoxBody, MessageBody, SizedStream}; - -use crate::BoxError; +use actix_http::body::{BodySize, BoxBody, MessageBody}; pin_project! { /// Represents various types of HTTP message body. @@ -160,91 +156,6 @@ impl fmt::Debug for AnyBody { } } -impl From<&'static str> for AnyBody { - fn from(string: &'static str) -> Self { - Self::Bytes { - body: Bytes::from_static(string.as_ref()), - } - } -} - -impl From<&'static [u8]> for AnyBody { - fn from(bytes: &'static [u8]) -> Self { - Self::Bytes { - body: Bytes::from_static(bytes), - } - } -} - -impl From> for AnyBody { - fn from(vec: Vec) -> Self { - Self::Bytes { - body: Bytes::from(vec), - } - } -} - -impl From for AnyBody { - fn from(string: String) -> Self { - Self::Bytes { - body: Bytes::from(string), - } - } -} - -impl From<&'_ String> for AnyBody { - fn from(string: &String) -> Self { - Self::Bytes { - body: Bytes::copy_from_slice(AsRef::<[u8]>::as_ref(&string)), - } - } -} - -impl From> for AnyBody { - fn from(string: Cow<'_, str>) -> Self { - match string { - Cow::Owned(s) => Self::from(s), - Cow::Borrowed(s) => Self::Bytes { - body: Bytes::copy_from_slice(AsRef::<[u8]>::as_ref(s)), - }, - } - } -} - -impl From for AnyBody { - fn from(bytes: Bytes) -> Self { - Self::Bytes { body: bytes } - } -} - -impl From for AnyBody { - fn from(bytes: BytesMut) -> Self { - Self::Bytes { - body: bytes.freeze(), - } - } -} - -impl From> for AnyBody -where - S: Stream> + 'static, - E: Into + 'static, -{ - fn from(stream: SizedStream) -> Self { - AnyBody::new_boxed(stream) - } -} - -impl From> for AnyBody -where - S: Stream> + 'static, - E: Into + 'static, -{ - fn from(stream: BodyStream) -> Self { - AnyBody::new_boxed(stream) - } -} - #[cfg(test)] mod tests { use std::marker::PhantomPinned; diff --git a/awc/src/frozen.rs b/awc/src/frozen.rs index 14ecf9f32..4023bd1c8 100644 --- a/awc/src/frozen.rs +++ b/awc/src/frozen.rs @@ -1,4 +1,4 @@ -use std::{convert::TryFrom, net, rc::Rc, time::Duration}; +use std::{net, rc::Rc, time::Duration}; use bytes::Bytes; use futures_core::Stream; @@ -7,7 +7,7 @@ use serde::Serialize; use actix_http::{ body::MessageBody, error::HttpError, - header::{HeaderMap, HeaderName, TryIntoHeaderValue}, + header::{HeaderMap, TryIntoHeaderPair}, Method, RequestHead, Uri, }; @@ -18,6 +18,7 @@ use crate::{ }; /// `FrozenClientRequest` struct represents cloneable client request. +/// /// It could be used to send same request multiple times. #[derive(Clone)] pub struct FrozenClientRequest { @@ -83,7 +84,7 @@ impl FrozenClientRequest { /// Send a streaming body. pub fn send_stream(&self, stream: S) -> SendClientRequest where - S: Stream> + Unpin + 'static, + S: Stream> + 'static, E: Into + 'static, { RequestSender::Rc(self.head.clone(), None).send_stream( @@ -105,20 +106,14 @@ impl FrozenClientRequest { ) } - /// Create a `FrozenSendBuilder` with extra headers + /// Clones this `FrozenClientRequest`, returning a new one with extra headers added. pub fn extra_headers(&self, extra_headers: HeaderMap) -> FrozenSendBuilder { FrozenSendBuilder::new(self.clone(), extra_headers) } - /// Create a `FrozenSendBuilder` with an extra header - pub fn extra_header(&self, key: K, value: V) -> FrozenSendBuilder - where - HeaderName: TryFrom, - >::Error: Into, - V: TryIntoHeaderValue, - { - self.extra_headers(HeaderMap::new()) - .extra_header(key, value) + /// Clones this `FrozenClientRequest`, returning a new one with the extra header added. + pub fn extra_header(&self, header: impl TryIntoHeaderPair) -> FrozenSendBuilder { + self.extra_headers(HeaderMap::new()).extra_header(header) } } @@ -139,29 +134,20 @@ impl FrozenSendBuilder { } /// Insert a header, it overrides existing header in `FrozenClientRequest`. - pub fn extra_header(mut self, key: K, value: V) -> Self - where - HeaderName: TryFrom, - >::Error: Into, - V: TryIntoHeaderValue, - { - match HeaderName::try_from(key) { - Ok(key) => match value.try_into_value() { - Ok(value) => { - self.extra_headers.insert(key, value); - } - Err(e) => self.err = Some(e.into()), - }, - Err(e) => self.err = Some(e.into()), + pub fn extra_header(mut self, header: impl TryIntoHeaderPair) -> Self { + match header.try_into_pair() { + Ok((key, value)) => { + self.extra_headers.insert(key, value); + } + + Err(err) => self.err = Some(err.into()), } + self } /// Complete request construction and send a body. - pub fn send_body(self, body: B) -> SendClientRequest - where - B: MessageBody + 'static, - { + pub fn send_body(self, body: impl MessageBody + 'static) -> SendClientRequest { if let Some(e) = self.err { return e.into(); } @@ -176,9 +162,9 @@ impl FrozenSendBuilder { } /// Complete request construction and send a json body. - pub fn send_json(self, value: &T) -> SendClientRequest { - if let Some(e) = self.err { - return e.into(); + pub fn send_json(self, value: impl Serialize) -> SendClientRequest { + if let Some(err) = self.err { + return err.into(); } RequestSender::Rc(self.req.head, Some(self.extra_headers)).send_json( @@ -191,7 +177,7 @@ impl FrozenSendBuilder { } /// Complete request construction and send an urlencoded body. - pub fn send_form(self, value: &T) -> SendClientRequest { + pub fn send_form(self, value: impl Serialize) -> SendClientRequest { if let Some(e) = self.err { return e.into(); } @@ -208,7 +194,7 @@ impl FrozenSendBuilder { /// Complete request construction and send a streaming body. pub fn send_stream(self, stream: S) -> SendClientRequest where - S: Stream> + Unpin + 'static, + S: Stream> + 'static, E: Into + 'static, { if let Some(e) = self.err { diff --git a/awc/src/middleware/redirect.rs b/awc/src/middleware/redirect.rs index 0ee969eee..704d2d79d 100644 --- a/awc/src/middleware/redirect.rs +++ b/awc/src/middleware/redirect.rs @@ -190,7 +190,9 @@ where let body_new = if is_redirect { // try to reuse body match body { - Some(ref bytes) => AnyBody::from(bytes.clone()), + Some(ref bytes) => AnyBody::Bytes { + body: bytes.clone(), + }, // TODO: should this be AnyBody::Empty or AnyBody::None. _ => AnyBody::empty(), } diff --git a/awc/src/request.rs b/awc/src/request.rs index 8824dd08a..8bcf1ee01 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -385,7 +385,7 @@ impl ClientRequest { /// Set an streaming body and generate `ClientRequest`. pub fn send_stream(self, stream: S) -> SendClientRequest where - S: Stream> + Unpin + 'static, + S: Stream> + 'static, E: Into + 'static, { let slf = match self.prep_for_sending() { diff --git a/awc/src/responses/response.rs b/awc/src/responses/response.rs index 6385aea19..0197265f1 100644 --- a/awc/src/responses/response.rs +++ b/awc/src/responses/response.rs @@ -7,8 +7,8 @@ use std::{ }; use actix_http::{ - error::PayloadError, header, header::HeaderMap, BoxedPayloadStream, Extensions, - HttpMessage, Payload, ResponseHead, StatusCode, Version, + error::PayloadError, header::HeaderMap, BoxedPayloadStream, Extensions, HttpMessage, + Payload, ResponseHead, StatusCode, Version, }; use actix_rt::time::{sleep, Sleep}; use bytes::Bytes; @@ -119,12 +119,13 @@ impl ClientResponse { if self.extensions().get::().is_none() { let mut cookies = Vec::new(); - for hdr in self.headers().get_all(&header::SET_COOKIE) { + for hdr in self.headers().get_all(&actix_http::header::SET_COOKIE) { let s = std::str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?; cookies.push(Cookie::parse_encoded(s)?.into_owned()); } self.extensions_mut().insert(Cookies(cookies)); } + Ok(Ref::map(self.extensions(), |ext| { &ext.get::().unwrap().0 })) diff --git a/awc/src/sender.rs b/awc/src/sender.rs index edf41163d..cd30e571d 100644 --- a/awc/src/sender.rs +++ b/awc/src/sender.rs @@ -181,17 +181,14 @@ pub(crate) enum RequestSender { } impl RequestSender { - pub(crate) fn send_body( + pub(crate) fn send_body( self, addr: Option, response_decompress: bool, timeout: Option, config: &ClientConfig, - body: B, - ) -> SendClientRequest - where - B: MessageBody + 'static, - { + body: impl MessageBody + 'static, + ) -> SendClientRequest { let req = match self { RequestSender::Owned(head) => ConnectRequest::Client( RequestHeadType::Owned(head), @@ -210,15 +207,15 @@ impl RequestSender { SendClientRequest::new(fut, response_decompress, timeout.or(config.timeout)) } - pub(crate) fn send_json( + pub(crate) fn send_json( mut self, addr: Option, response_decompress: bool, timeout: Option, config: &ClientConfig, - value: &T, + value: impl Serialize, ) -> SendClientRequest { - let body = match serde_json::to_string(value) { + let body = match serde_json::to_string(&value) { Ok(body) => body, Err(err) => return PrepForSendingError::Json(err).into(), }; @@ -227,22 +224,16 @@ impl RequestSender { return e.into(); } - self.send_body( - addr, - response_decompress, - timeout, - config, - AnyBody::from_message_body(body.into_bytes()), - ) + self.send_body(addr, response_decompress, timeout, config, body) } - pub(crate) fn send_form( + pub(crate) fn send_form( mut self, addr: Option, response_decompress: bool, timeout: Option, config: &ClientConfig, - value: &T, + value: impl Serialize, ) -> SendClientRequest { let body = match serde_urlencoded::to_string(value) { Ok(body) => body, @@ -250,19 +241,13 @@ impl RequestSender { }; // set content-type - if let Err(e) = + if let Err(err) = self.set_header_if_none(header::CONTENT_TYPE, "application/x-www-form-urlencoded") { - return e.into(); + return err.into(); } - self.send_body( - addr, - response_decompress, - timeout, - config, - AnyBody::from_message_body(body.into_bytes()), - ) + self.send_body(addr, response_decompress, timeout, config, body) } pub(crate) fn send_stream( @@ -274,7 +259,7 @@ impl RequestSender { stream: S, ) -> SendClientRequest where - S: Stream> + Unpin + 'static, + S: Stream> + 'static, E: Into + 'static, { self.send_body( @@ -282,7 +267,7 @@ impl RequestSender { response_decompress, timeout, config, - AnyBody::new_boxed(BodyStream::new(stream)), + BodyStream::new(stream), ) } @@ -293,7 +278,7 @@ impl RequestSender { timeout: Option, config: &ClientConfig, ) -> SendClientRequest { - self.send_body(addr, response_decompress, timeout, config, AnyBody::empty()) + self.send_body(addr, response_decompress, timeout, config, ()) } fn set_header_if_none(&mut self, key: HeaderName, value: V) -> Result<(), HttpError> From 0f5c876c6bb764635f27280056c47f90382bccf6 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 28 Dec 2021 14:50:48 +0000 Subject: [PATCH 83/87] tweak guard docs --- src/guard.rs | 70 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/src/guard.rs b/src/guard.rs index fb3e4f243..ebda69cb9 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -10,16 +10,22 @@ //! object and returns a boolean; true if the request _should_ be handled by the guarded service //! or handler. This interface is defined by the [`Guard`] trait. //! -//! Commonly-used guards are provided in this module as well as way of creating a guard from a +//! Commonly-used guards are provided in this module as well as a way of creating a guard from a //! closure ([`fn_guard`]). The [`Not`], [`Any`], and [`All`] guards are noteworthy, as they can be //! used to compose other guards in a more flexible and semantic way than calling `.guard(...)` on //! services multiple times (which might have different combining behavior than you want). //! +//! There are shortcuts for routes with method guards in the [`web`](crate::web) module: +//! [`web::get()`](crate::web::get), [`web::post()`](crate::web::post), etc. The routes created by +//! the following calls are equivalent: +//! - `web::get()` (recommended form) +//! - `web::route().guard(guard::Get())` +//! //! Guards can not modify anything about the request. However, it is possible to store extra //! attributes in the request-local data container obtained with [`GuardContext::req_data_mut`]. //! -//! Guards can prevent resource definitions from overlapping (resulting in some inaccessible routes) -//! where they otherwise would when only considering paths. See the virtual hosting example below. +//! Guards can prevent resource definitions from overlapping which, when only considering paths, +//! would result in inaccessible routes. See the [`Host`] guard for an example of virtual hosting. //! //! # Examples //! In the following code, the `/guarded` resource has one defined route whose handler will only be @@ -36,31 +42,9 @@ //! ); //! ``` //! -//! Guards can be used to set up some form of [virtual hosting] within a single app. -//! Overlapping scope prefixes are usually discouraged, but when combined with non-overlapping guard -//! definitions they become safe to use in this way. Without these host guards, only routes under -//! the first-to-be-defined scope would be accessible. You can test this locally using `127.0.0.1` -//! and `localhost` as the `Host` guards. -//! ``` -//! use actix_web::{web, http::Method, guard, App, HttpResponse}; -//! -//! App::new() -//! .service( -//! web::scope("") -//! .guard(guard::Host("www.rust-lang.org")) -//! .default_service(web::to(|| HttpResponse::Ok().body("marketing site"))), -//! ) -//! .service( -//! web::scope("") -//! .guard(guard::Host("play.rust-lang.org")) -//! .default_service(web::to(|| HttpResponse::Ok().body("playground frontend"))), -//! ); -//! ``` -//! //! [`Scope`]: crate::Scope::guard() //! [`Resource`]: crate::Resource::guard() //! [`Route`]: crate::Route::guard() -//! [virtual hosting]: https://en.wikipedia.org/wiki/Virtual_hosting use std::{ cell::{Ref, RefMut}, @@ -373,6 +357,29 @@ impl Guard for HeaderGuard { /// .guard(Host("admin.rust-lang.org").scheme("https")) /// .default_service(web::to(|| HttpResponse::Ok().body("admin connection is secure"))); /// ``` +/// +/// The `Host` guard can be used to set up some form of [virtual hosting] within a single app. +/// Overlapping scope prefixes are usually discouraged, but when combined with non-overlapping guard +/// definitions they become safe to use in this way. Without these host guards, only routes under +/// the first-to-be-defined scope would be accessible. You can test this locally using `127.0.0.1` +/// and `localhost` as the `Host` guards. +/// ``` +/// use actix_web::{web, http::Method, guard, App, HttpResponse}; +/// +/// App::new() +/// .service( +/// web::scope("") +/// .guard(guard::Host("www.rust-lang.org")) +/// .default_service(web::to(|| HttpResponse::Ok().body("marketing site"))), +/// ) +/// .service( +/// web::scope("") +/// .guard(guard::Host("play.rust-lang.org")) +/// .default_service(web::to(|| HttpResponse::Ok().body("playground frontend"))), +/// ); +/// ``` +/// +/// [virtual hosting]: https://en.wikipedia.org/wiki/Virtual_hosting #[allow(non_snake_case)] pub fn Host(host: impl AsRef) -> HostGuard { HostGuard { @@ -642,4 +649,17 @@ mod tests { let req = TestRequest::default().uri("crates.io").to_srv_request(); assert!(!guard.check(&req.guard_ctx())); } + + #[test] + fn mega_nesting() { + let guard = fn_guard(|ctx| All(Not(Any(Not(Trace())))).check(ctx)); + + let req = TestRequest::default().to_srv_request(); + assert!(!guard.check(&req.guard_ctx())); + + let req = TestRequest::default() + .method(Method::TRACE) + .to_srv_request(); + assert!(guard.check(&req.guard_ctx())); + } } From 2b2de298001c7718952a7e8647f6b1a2736c7259 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 28 Dec 2021 14:52:43 +0000 Subject: [PATCH 84/87] never return port in `realip_remote_addr` (#2554) --- CHANGES.md | 6 ++ src/config.rs | 4 +- src/info.rs | 125 ++++++++++++++++++++++++--------------- src/middleware/logger.rs | 2 +- src/request.rs | 19 +++--- 5 files changed, 97 insertions(+), 59 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f925f3b94..b6d3b103d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,7 +11,13 @@ - Some guards now return `impl Guard` and their concrete types are made private: `guard::{Header}` and all the method guards. [#2552] - The `Not` guard is now generic over the type of guard it wraps. [#2552] +### Fixed +- Rename `ConnectionInfo::{remote_addr => peer_addr}`, deprecating the old name. [#2554] +- `ConnectionInfo::peer_addr` will not return the port number. [#2554] +- `ConnectionInfo::realip_remote_addr` will not return the port number if sourcing the IP from the peer's socket address. [#2554] + [#2552]: https://github.com/actix/actix-web/pull/2552 +[#2554]: https://github.com/actix/actix-web/pull/2554 ## 4.0.0-beta.16 - 2021-12-27 diff --git a/src/config.rs b/src/config.rs index cfa9a4ca3..d68374387 100644 --- a/src/config.rs +++ b/src/config.rs @@ -128,7 +128,7 @@ impl AppConfig { /// Server host name. /// - /// Host name is used by application router as a hostname for url generation. + /// Host name is used by application router as a hostname for URL generation. /// Check [ConnectionInfo](super::dev::ConnectionInfo::host()) /// documentation for more information. /// @@ -137,7 +137,7 @@ impl AppConfig { &self.host } - /// Returns true if connection is secure(https) + /// Returns true if connection is secure (i.e., running over `https:`). pub fn secure(&self) -> bool { self.secure } diff --git a/src/info.rs b/src/info.rs index 71194b24d..ce1ef97c6 100644 --- a/src/info.rs +++ b/src/info.rs @@ -67,7 +67,7 @@ fn first_header_value<'a>(req: &'a RequestHead, name: &'_ HeaderName) -> Option< pub struct ConnectionInfo { host: String, scheme: String, - remote_addr: Option, + peer_addr: Option, realip_remote_addr: Option, } @@ -134,67 +134,70 @@ impl ConnectionInfo { .or_else(|| first_header_value(req, &*X_FORWARDED_FOR)) .map(str::to_owned); - let remote_addr = req.peer_addr.map(|addr| addr.to_string()); + let peer_addr = req.peer_addr.map(|addr| addr.ip().to_string()); ConnectionInfo { host, scheme, - remote_addr, + peer_addr, realip_remote_addr, } } + /// Real IP (remote address) of client that initiated request. + /// + /// The address is resolved through the following, in order: + /// - `Forwarded` header + /// - `X-Forwarded-For` header + /// - peer address of opened socket (same as [`remote_addr`](Self::remote_addr)) + /// + /// # Security + /// Do not use this function for security purposes unless you can be sure that the `Forwarded` + /// and `X-Forwarded-For` headers cannot be spoofed by the client. If you are running without a + /// proxy then [obtaining the peer address](Self::peer_addr) would be more appropriate. + #[inline] + pub fn realip_remote_addr(&self) -> Option<&str> { + self.realip_remote_addr + .as_deref() + .or_else(|| self.peer_addr.as_deref()) + } + + /// Returns serialized IP address of the peer connection. + /// + /// See [`HttpRequest::peer_addr`] for more details. + #[inline] + pub fn peer_addr(&self) -> Option<&str> { + self.peer_addr.as_deref() + } + + /// Hostname of the request. + /// + /// Hostname is resolved through the following, in order: + /// - `Forwarded` header + /// - `X-Forwarded-Host` header + /// - `Host` header + /// - request target / URI + /// - configured server hostname + #[inline] + pub fn host(&self) -> &str { + &self.host + } + /// Scheme of the request. /// - /// Scheme is resolved through the following headers, in this order: - /// - /// - Forwarded - /// - X-Forwarded-Proto - /// - Uri + /// Scheme is resolved through the following, in order: + /// - `Forwarded` header + /// - `X-Forwarded-Proto` header + /// - request target / URI #[inline] pub fn scheme(&self) -> &str { &self.scheme } - /// Hostname of the request. - /// - /// Hostname is resolved through the following headers, in this order: - /// - /// - Forwarded - /// - X-Forwarded-Host - /// - Host - /// - Uri - /// - Server hostname - pub fn host(&self) -> &str { - &self.host - } - - /// Remote address of the connection. - /// - /// Get remote_addr address from socket address. + #[doc(hidden)] + #[deprecated(since = "4.0.0", note = "Renamed to `peer_addr`.")] pub fn remote_addr(&self) -> Option<&str> { - self.remote_addr.as_deref() - } - - /// Real IP (remote address) of client that initiated request. - /// - /// The address is resolved through the following headers, in this order: - /// - /// - Forwarded - /// - X-Forwarded-For - /// - remote_addr name of opened socket - /// - /// # Security - /// Do not use this function for security purposes, unless you can ensure the Forwarded and - /// X-Forwarded-For headers cannot be spoofed by the client. If you want the client's socket - /// address explicitly, use [`HttpRequest::peer_addr()`][peer_addr] instead. - /// - /// [peer_addr]: crate::web::HttpRequest::peer_addr() - #[inline] - pub fn realip_remote_addr(&self) -> Option<&str> { - self.realip_remote_addr - .as_deref() - .or_else(|| self.remote_addr.as_deref()) + self.peer_addr() } } @@ -209,7 +212,7 @@ impl FromRequest for ConnectionInfo { /// Extractor for peer's socket address. /// -/// Also see [`HttpRequest::peer_addr`]. +/// Also see [`HttpRequest::peer_addr`] and [`ConnectionInfo::peer_addr`]. /// /// # Examples /// ``` @@ -432,13 +435,37 @@ mod tests { #[actix_rt::test] async fn peer_addr_extract() { + let req = TestRequest::default().to_http_request(); + let res = PeerAddr::extract(&req).await; + assert!(res.is_err()); + let addr = "127.0.0.1:8080".parse().unwrap(); let req = TestRequest::default().peer_addr(addr).to_http_request(); let peer_addr = PeerAddr::extract(&req).await.unwrap(); assert_eq!(peer_addr, PeerAddr(addr)); + } + #[actix_rt::test] + async fn remote_address() { let req = TestRequest::default().to_http_request(); - let res = PeerAddr::extract(&req).await; - assert!(res.is_err()); + let res = ConnectionInfo::extract(&req).await.unwrap(); + assert!(res.peer_addr().is_none()); + + let addr = "127.0.0.1:8080".parse().unwrap(); + let req = TestRequest::default().peer_addr(addr).to_http_request(); + let conn_info = ConnectionInfo::extract(&req).await.unwrap(); + assert_eq!(conn_info.peer_addr().unwrap(), "127.0.0.1"); + } + + #[actix_rt::test] + async fn real_ip_from_socket_addr() { + let req = TestRequest::default().to_http_request(); + let res = ConnectionInfo::extract(&req).await.unwrap(); + assert!(res.realip_remote_addr().is_none()); + + let addr = "127.0.0.1:8080".parse().unwrap(); + let req = TestRequest::default().peer_addr(addr).to_http_request(); + let conn_info = ConnectionInfo::extract(&req).await.unwrap(); + assert_eq!(conn_info.realip_remote_addr().unwrap(), "127.0.0.1"); } } diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index d7fdb234f..969cb0c10 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -547,7 +547,7 @@ impl FormatText { *self = FormatText::Str(s.to_string()); } FormatText::RemoteAddr => { - let s = if let Some(peer) = req.connection_info().remote_addr() { + let s = if let Some(peer) = req.connection_info().peer_addr() { FormatText::Str((*peer).to_string()) } else { FormatText::Str("-".to_string()) diff --git a/src/request.rs b/src/request.rs index 07fb4eb2d..b59369317 100644 --- a/src/request.rs +++ b/src/request.rs @@ -228,23 +228,28 @@ impl HttpRequest { self.app_state().rmap() } - /// Peer socket address. + /// Returns peer socket address. /// /// Peer address is the directly connected peer's socket address. If a proxy is used in front of /// the Actix Web server, then it would be address of this proxy. /// - /// To get client connection information `.connection_info()` should be used. + /// For expanded client connection information, use [`connection_info`] instead. /// - /// Will only return None when called in unit tests. + /// Will only return None when called in unit tests unless [`TestRequest::peer_addr`] is used. + /// + /// [`TestRequest::peer_addr`]: crate::test::TestRequest::peer_addr + /// [`connection_info`]: Self::connection_info #[inline] pub fn peer_addr(&self) -> Option { self.head().peer_addr } - /// Get *ConnectionInfo* for the current request. + /// Returns connection info for the current request. /// - /// This method panics if request's extensions container is already - /// borrowed. + /// The return type, [`ConnectionInfo`], can also be used as an extractor. + /// + /// # Panics + /// Panics if request's extensions container is already borrowed. #[inline] pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> { if !self.extensions().contains::() { @@ -252,7 +257,7 @@ impl HttpRequest { self.extensions_mut().insert(info); } - Ref::map(self.extensions(), |e| e.get().unwrap()) + Ref::map(self.extensions(), |data| data.get().unwrap()) } /// App config From 798e9911e948857e15df695a7691df2a146b1120 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 29 Dec 2021 07:07:46 +0000 Subject: [PATCH 85/87] prepare awc release 3.0.0-beta.16 --- awc/CHANGES.md | 3 +++ awc/Cargo.toml | 4 ++-- awc/README.md | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/awc/CHANGES.md b/awc/CHANGES.md index 06e94292a..212469873 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 3.0.0-beta.16 - 2021-12-29 - `*::send_json` and `*::send_form` methods now receive `impl Serialize`. [#2553] - `FrozenClientRequest::extra_header` now uses receives an `impl TryIntoHeaderPair`. [#2553] - Remove unnecessary `Unpin` bounds on `*::send_stream`. [#2553] diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 8777ffa74..676a10895 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "3.0.0-beta.15" +version = "3.0.0-beta.16" authors = [ "Nikolay Kim ", "fakeshadow <24548779@qq.com>", @@ -99,7 +99,7 @@ actix-server = "2.0.0-rc.2" actix-test = { version = "0.1.0-beta.10", features = ["openssl", "rustls"] } actix-tls = { version = "3.0.0", features = ["openssl", "rustls"] } actix-utils = "3.0.0" -actix-web = { version = "4.0.0-beta.16", features = ["openssl"] } +actix-web = { version = "4.0.0-beta.17", features = ["openssl"] } brotli2 = "0.3.2" env_logger = "0.9" diff --git a/awc/README.md b/awc/README.md index 582ecb18f..4916210e4 100644 --- a/awc/README.md +++ b/awc/README.md @@ -3,9 +3,9 @@ > Async HTTP and WebSocket client library. [![crates.io](https://img.shields.io/crates/v/awc?label=latest)](https://crates.io/crates/awc) -[![Documentation](https://docs.rs/awc/badge.svg?version=3.0.0-beta.15)](https://docs.rs/awc/3.0.0-beta.15) +[![Documentation](https://docs.rs/awc/badge.svg?version=3.0.0-beta.16)](https://docs.rs/awc/3.0.0-beta.16) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/awc) -[![Dependency Status](https://deps.rs/crate/awc/3.0.0-beta.15/status.svg)](https://deps.rs/crate/awc/3.0.0-beta.15) +[![Dependency Status](https://deps.rs/crate/awc/3.0.0-beta.16/status.svg)](https://deps.rs/crate/awc/3.0.0-beta.16) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) ## Documentation & Resources From 11d50d792b6569f190f9873f3cee5c5569b75e0f Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 29 Dec 2021 07:07:51 +0000 Subject: [PATCH 86/87] prepare actix-web release 4.0.0-beta.17 --- CHANGES.md | 5 ++++- Cargo.toml | 4 ++-- README.md | 4 ++-- actix-files/Cargo.toml | 4 ++-- actix-http-test/Cargo.toml | 4 ++-- actix-http/Cargo.toml | 2 +- actix-multipart/Cargo.toml | 2 +- actix-test/Cargo.toml | 4 ++-- actix-web-actors/Cargo.toml | 4 ++-- actix-web-codegen/Cargo.toml | 2 +- 10 files changed, 19 insertions(+), 16 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b6d3b103d..c870f10e7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 4.0.0-beta.17 - 2021-12-29 ### Added - `guard::GuardContext` for use with the `Guard` trait. [#2552] - `ServiceRequest::guard_ctx` for obtaining a guard context. [#2552] @@ -8,7 +11,7 @@ ### Changed - `Guard` trait now receives a `&GuardContext`. [#2552] - `guard::fn_guard` functions now receives a `&GuardContext`. [#2552] -- Some guards now return `impl Guard` and their concrete types are made private: `guard::{Header}` and all the method guards. [#2552] +- Some guards now return `impl Guard` and their concrete types are made private: `guard::Header` and all the method guards. [#2552] - The `Not` guard is now generic over the type of guard it wraps. [#2552] ### Fixed diff --git a/Cargo.toml b/Cargo.toml index b6ef184e0..1b85e8e75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "4.0.0-beta.16" +version = "4.0.0-beta.17" authors = ["Nikolay Kim "] description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust" keywords = ["actix", "http", "web", "framework", "async"] @@ -107,7 +107,7 @@ url = "2.1" [dev-dependencies] actix-test = { version = "0.1.0-beta.10", features = ["openssl", "rustls"] } -awc = { version = "3.0.0-beta.15", features = ["openssl"] } +awc = { version = "3.0.0-beta.16", features = ["openssl"] } brotli2 = "0.3.2" criterion = { version = "0.3", features = ["html_reports"] } diff --git a/README.md b/README.md index f9d388f8b..afe6b1f8e 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,10 @@

[![crates.io](https://img.shields.io/crates/v/actix-web?label=latest)](https://crates.io/crates/actix-web) -[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.0.0-beta.16)](https://docs.rs/actix-web/4.0.0-beta.16) +[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.0.0-beta.17)](https://docs.rs/actix-web/4.0.0-beta.17) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-web.svg) -[![Dependency Status](https://deps.rs/crate/actix-web/4.0.0-beta.16/status.svg)](https://deps.rs/crate/actix-web/4.0.0-beta.16) +[![Dependency Status](https://deps.rs/crate/actix-web/4.0.0-beta.17/status.svg)](https://deps.rs/crate/actix-web/4.0.0-beta.17)
[![build status](https://github.com/actix/actix-web/workflows/CI%20%28Linux%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-web/actions) [![codecov](https://codecov.io/gh/actix/actix-web/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-web) diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index bbd4fee22..b7bb3fd07 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -25,7 +25,7 @@ experimental-io-uring = ["actix-web/experimental-io-uring", "tokio-uring"] actix-http = "3.0.0-beta.17" actix-service = "2" actix-utils = "3" -actix-web = { version = "4.0.0-beta.16", default-features = false } +actix-web = { version = "4.0.0-beta.17", default-features = false } askama_escape = "0.10" bitflags = "1" @@ -44,4 +44,4 @@ tokio-uring = { version = "0.1", optional = true } [dev-dependencies] actix-rt = "2.2" actix-test = "0.1.0-beta.10" -actix-web = "4.0.0-beta.16" +actix-web = "4.0.0-beta.17" diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index 5c58978ea..2883a8f7e 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -35,7 +35,7 @@ actix-tls = "3.0.0" actix-utils = "3.0.0" actix-rt = "2.2" actix-server = "2.0.0-rc.2" -awc = { version = "3.0.0-beta.15", default-features = false } +awc = { version = "3.0.0-beta.16", default-features = false } base64 = "0.13" bytes = "1" @@ -51,5 +51,5 @@ tls-openssl = { version = "0.10.9", package = "openssl", optional = true } tokio = { version = "1.8.4", features = ["sync"] } [dev-dependencies] -actix-web = { version = "4.0.0-beta.16", default-features = false, features = ["cookies"] } +actix-web = { version = "4.0.0-beta.17", default-features = false, features = ["cookies"] } actix-http = "3.0.0-beta.17" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index 9e587890b..9575f55e7 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -82,7 +82,7 @@ zstd = { version = "0.9", optional = true } actix-http-test = { version = "3.0.0-beta.10", features = ["openssl"] } actix-server = "2.0.0-rc.2" actix-tls = { version = "3.0.0", features = ["openssl"] } -actix-web = "4.0.0-beta.16" +actix-web = "4.0.0-beta.17" async-stream = "0.3" criterion = { version = "0.3", features = ["html_reports"] } diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index de13133a1..4beddd0b8 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -15,7 +15,7 @@ path = "src/lib.rs" [dependencies] actix-utils = "3.0.0" -actix-web = { version = "4.0.0-beta.16", default-features = false } +actix-web = { version = "4.0.0-beta.17", default-features = false } bytes = "1" derive_more = "0.99.5" diff --git a/actix-test/Cargo.toml b/actix-test/Cargo.toml index c7177a38c..c523a6566 100644 --- a/actix-test/Cargo.toml +++ b/actix-test/Cargo.toml @@ -34,8 +34,8 @@ actix-http-test = "3.0.0-beta.10" actix-rt = "2.1" actix-service = "2.0.0" actix-utils = "3.0.0" -actix-web = { version = "4.0.0-beta.16", default-features = false, features = ["cookies"] } -awc = { version = "3.0.0-beta.15", default-features = false, features = ["cookies"] } +actix-web = { version = "4.0.0-beta.17", default-features = false, features = ["cookies"] } +awc = { version = "3.0.0-beta.16", default-features = false, features = ["cookies"] } futures-core = { version = "0.3.7", default-features = false, features = ["std"] } futures-util = { version = "0.3.7", default-features = false, features = [] } diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 3b792093a..719c563cb 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" actix = { version = "0.12.0", default-features = false } actix-codec = "0.4.1" actix-http = "3.0.0-beta.17" -actix-web = { version = "4.0.0-beta.16", default-features = false } +actix-web = { version = "4.0.0-beta.17", default-features = false } bytes = "1" bytestring = "1" @@ -28,7 +28,7 @@ tokio = { version = "1.8.4", features = ["sync"] } [dev-dependencies] actix-rt = "2.2" actix-test = "0.1.0-beta.10" -awc = { version = "3.0.0-beta.15", default-features = false } +awc = { version = "3.0.0-beta.16", default-features = false } env_logger = "0.9" futures-util = { version = "0.3.7", default-features = false } diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index 5250baa90..b014a47ae 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -25,7 +25,7 @@ actix-macros = "0.2.3" actix-rt = "2.2" actix-test = "0.1.0-beta.10" actix-utils = "3.0.0" -actix-web = "4.0.0-beta.16" +actix-web = "4.0.0-beta.17" futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } trybuild = "1" From 9779010a5af14a7ccbe3668280f117de5345769a Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 29 Dec 2021 07:08:10 +0000 Subject: [PATCH 87/87] prepare actix-files release 0.6.0-beta.12 --- actix-files/CHANGES.md | 4 ++++ actix-files/Cargo.toml | 2 +- actix-files/README.md | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index af6dcb415..65007c955 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 0.6.0-beta.12 - 2021-12-29 +* No significant changes since `0.6.0-beta.11`. + + ## 0.6.0-beta.11 - 2021-12-27 * No significant changes since `0.6.0-beta.10`. diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index b7bb3fd07..adf4408ba 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.6.0-beta.11" +version = "0.6.0-beta.12" authors = [ "Nikolay Kim ", "fakeshadow <24548779@qq.com>", diff --git a/actix-files/README.md b/actix-files/README.md index db5c94d1e..3f310a607 100644 --- a/actix-files/README.md +++ b/actix-files/README.md @@ -3,11 +3,11 @@ > Static file serving for Actix Web [![crates.io](https://img.shields.io/crates/v/actix-files?label=latest)](https://crates.io/crates/actix-files) -[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.0-beta.11)](https://docs.rs/actix-files/0.6.0-beta.11) +[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.0-beta.12)](https://docs.rs/actix-files/0.6.0-beta.12) [![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html) ![License](https://img.shields.io/crates/l/actix-files.svg)
-[![dependency status](https://deps.rs/crate/actix-files/0.6.0-beta.11/status.svg)](https://deps.rs/crate/actix-files/0.6.0-beta.11) +[![dependency status](https://deps.rs/crate/actix-files/0.6.0-beta.12/status.svg)](https://deps.rs/crate/actix-files/0.6.0-beta.12) [![Download](https://img.shields.io/crates/d/actix-files.svg)](https://crates.io/crates/actix-files) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)