From 5e811053173a6442e1e228ef8025f4704e4a9216 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Mon, 8 Mar 2021 12:00:20 -0800 Subject: [PATCH 01/36] remove ka timer from h2 dispatcher (#2057) --- actix-http/src/h2/dispatcher.rs | 110 +++++++++++--------------------- actix-http/src/h2/service.rs | 1 - actix-http/src/service.rs | 1 - 3 files changed, 37 insertions(+), 75 deletions(-) diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 8e01afcb5..958c761d5 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -2,7 +2,6 @@ use std::task::{Context, Poll}; use std::{cmp, future::Future, marker::PhantomData, net, pin::Pin, rc::Rc}; use actix_codec::{AsyncRead, AsyncWrite}; -use actix_rt::time::{Instant, Sleep}; use actix_service::Service; use bytes::{Bytes, BytesMut}; use futures_core::ready; @@ -36,8 +35,6 @@ where on_connect_data: OnConnectData, config: ServiceConfig, peer_addr: Option, - ka_expire: Instant, - ka_timer: Option, _phantom: PhantomData, } @@ -54,33 +51,14 @@ where connection: Connection, on_connect_data: OnConnectData, config: ServiceConfig, - timeout: Option, peer_addr: Option, ) -> Self { - // let keepalive = config.keep_alive_enabled(); - // let flags = if keepalive { - // Flags::KEEPALIVE | Flags::KEEPALIVE_ENABLED - // } else { - // Flags::empty() - // }; - - // keep-alive timer - let (ka_expire, ka_timer) = if let Some(delay) = timeout { - (delay.deadline(), Some(delay)) - } else if let Some(delay) = config.keep_alive_timer() { - (delay.deadline(), Some(delay)) - } else { - (config.now(), None) - }; - Dispatcher { flow, config, peer_addr, connection, on_connect_data, - ka_expire, - ka_timer, _phantom: PhantomData, } } @@ -108,13 +86,6 @@ where Some(Err(err)) => return Poll::Ready(Err(err.into())), Some(Ok((req, res))) => { - // update keep-alive expire - if this.ka_timer.is_some() { - if let Some(expire) = this.config.keep_alive_expire() { - this.ka_expire = expire; - } - } - let (parts, body) = req.into_parts(); let pl = crate::h2::Payload::new(body); let pl = Payload::::H2(pl); @@ -130,7 +101,7 @@ where // merge on_connect_ext data into request extensions this.on_connect_data.merge_into(&mut req); - let svc = ServiceResponse:: { + let svc = ServiceResponse { state: ServiceResponseState::ServiceCall( this.flow.service.call(req), Some(res), @@ -312,57 +283,50 @@ where ServiceResponseStateProj::SendPayload(ref mut stream, ref mut body) => { loop { - loop { - match this.buffer { - Some(ref mut buffer) => { - match ready!(stream.poll_capacity(cx)) { - None => return Poll::Ready(()), + match this.buffer { + Some(ref mut buffer) => match ready!(stream.poll_capacity(cx)) { + None => return Poll::Ready(()), - Some(Ok(cap)) => { - let len = buffer.len(); - let bytes = buffer.split_to(cmp::min(cap, len)); + Some(Ok(cap)) => { + let len = buffer.len(); + let bytes = buffer.split_to(cmp::min(cap, len)); - if let Err(e) = stream.send_data(bytes, false) { - warn!("{:?}", e); - return Poll::Ready(()); - } else if !buffer.is_empty() { - let cap = cmp::min(buffer.len(), CHUNK_SIZE); - stream.reserve_capacity(cap); - } else { - this.buffer.take(); - } - } - - Some(Err(e)) => { - warn!("{:?}", e); - return Poll::Ready(()); - } + if let Err(e) = stream.send_data(bytes, false) { + warn!("{:?}", e); + return Poll::Ready(()); + } else if !buffer.is_empty() { + let cap = cmp::min(buffer.len(), CHUNK_SIZE); + stream.reserve_capacity(cap); + } else { + this.buffer.take(); } } - None => match ready!(body.as_mut().poll_next(cx)) { - None => { - if let Err(e) = stream.send_data(Bytes::new(), true) - { - warn!("{:?}", e); - } - return Poll::Ready(()); - } + Some(Err(e)) => { + warn!("{:?}", e); + return Poll::Ready(()); + } + }, - Some(Ok(chunk)) => { - stream.reserve_capacity(cmp::min( - chunk.len(), - CHUNK_SIZE, - )); - *this.buffer = Some(chunk); + None => match ready!(body.as_mut().poll_next(cx)) { + None => { + if let Err(e) = stream.send_data(Bytes::new(), true) { + warn!("{:?}", e); } + return Poll::Ready(()); + } - Some(Err(e)) => { - error!("Response payload stream error: {:?}", e); - return Poll::Ready(()); - } - }, - } + Some(Ok(chunk)) => { + stream + .reserve_capacity(cmp::min(chunk.len(), CHUNK_SIZE)); + *this.buffer = Some(chunk); + } + + Some(Err(e)) => { + error!("Response payload stream error: {:?}", e); + return Poll::Ready(()); + } + }, } } } diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index 0984b3f23..1dc290e49 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -368,7 +368,6 @@ where conn, on_connect_data, config.take().unwrap(), - None, *peer_addr, )); self.poll(cx) diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 402affb7e..89f3e3bb1 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -658,7 +658,6 @@ where conn, on_connect_data, cfg, - None, peer_addr, ))); self.poll(cx) From 95130fcfd00a4344a44ebaccee54eb967098bf89 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 8 Mar 2021 20:32:19 +0000 Subject: [PATCH 02/36] address clippy warnings --- awc/src/ws.rs | 2 +- examples/basic.rs | 2 +- examples/uds.rs | 2 +- src/middleware/condition.rs | 1 + src/middleware/err_handlers.rs | 2 ++ tests/test_httpserver.rs | 4 ++-- 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/awc/src/ws.rs b/awc/src/ws.rs index 1aa426ac7..f64e9e19a 100644 --- a/awc/src/ws.rs +++ b/awc/src/ws.rs @@ -382,7 +382,7 @@ impl WebsocketsRequest { if let Some(hdr_key) = head.headers.get(&header::SEC_WEBSOCKET_ACCEPT) { let encoded = ws::hash_key(key.as_ref()); - if hdr_key.as_bytes() != &encoded { + if hdr_key.as_bytes() != encoded { log::trace!( "Invalid challenge response: expected: {:?} received: {:?}", &encoded, diff --git a/examples/basic.rs b/examples/basic.rs index 99eef3ee1..796f002e8 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -30,7 +30,7 @@ async fn main() -> std::io::Result<()> { .service( web::resource("/resource2/index.html") .wrap(middleware::DefaultHeaders::new().header("X-Version-R2", "0.3")) - .default_service(web::route().to(|| HttpResponse::MethodNotAllowed())) + .default_service(web::route().to(HttpResponse::MethodNotAllowed)) .route(web::get().to(index_async)), ) .service(web::resource("/test1.html").to(|| async { "Test\r\n" })) diff --git a/examples/uds.rs b/examples/uds.rs index 096781984..1db252fef 100644 --- a/examples/uds.rs +++ b/examples/uds.rs @@ -34,7 +34,7 @@ async fn main() -> std::io::Result<()> { .service( web::resource("/resource2/index.html") .wrap(middleware::DefaultHeaders::new().header("X-Version-R2", "0.3")) - .default_service(web::route().to(|| HttpResponse::MethodNotAllowed())) + .default_service(web::route().to(HttpResponse::MethodNotAllowed)) .route(web::get().to(index_async)), ) .service(web::resource("/test1.html").to(|| async { "Test\r\n" })) diff --git a/src/middleware/condition.rs b/src/middleware/condition.rs index f0c344062..63a90c853 100644 --- a/src/middleware/condition.rs +++ b/src/middleware/condition.rs @@ -106,6 +106,7 @@ mod tests { HttpResponse, }; + #[allow(clippy::unnecessary_wraps)] fn render_500(mut res: ServiceResponse) -> Result> { res.response_mut() .headers_mut() diff --git a/src/middleware/err_handlers.rs b/src/middleware/err_handlers.rs index 4673ed4ce..06e88cefd 100644 --- a/src/middleware/err_handlers.rs +++ b/src/middleware/err_handlers.rs @@ -182,6 +182,7 @@ mod tests { use crate::test::{self, TestRequest}; use crate::HttpResponse; + #[allow(clippy::unnecessary_wraps)] fn render_500(mut res: ServiceResponse) -> Result> { res.response_mut() .headers_mut() @@ -205,6 +206,7 @@ mod tests { assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); } + #[allow(clippy::unnecessary_wraps)] fn render_500_async( mut res: ServiceResponse, ) -> Result> { diff --git a/tests/test_httpserver.rs b/tests/test_httpserver.rs index 043159376..b6c0528dd 100644 --- a/tests/test_httpserver.rs +++ b/tests/test_httpserver.rs @@ -72,7 +72,7 @@ async fn test_start() { } #[cfg(feature = "openssl")] -fn ssl_acceptor() -> std::io::Result { +fn ssl_acceptor() -> SslAcceptorBuilder { use openssl::{ pkey::PKey, ssl::{SslAcceptor, SslMethod}, @@ -102,7 +102,7 @@ async fn test_start_ssl() { thread::spawn(move || { let sys = actix_rt::System::new(); - let builder = ssl_acceptor().unwrap(); + let builder = ssl_acceptor(); let srv = HttpServer::new(|| { App::new().service(web::resource("/").route(web::to(|req: HttpRequest| { From effacf8fc8ee8aedf1a5c809dbb1b066aad7a862 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 8 Mar 2021 20:51:50 +0000 Subject: [PATCH 03/36] fix ssl test --- actix-http/tests/test_server.rs | 4 ++-- tests/test_httpserver.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index 910fa81f2..a4c1f92b5 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -126,7 +126,7 @@ async fn test_chunked_payload() { .take_payload() .map(|res| match res { Ok(pl) => pl, - Err(e) => panic!(format!("Error reading payload: {}", e)), + Err(e) => panic!("Error reading payload: {}", e), }) .fold(0usize, |acc, chunk| ready(acc + chunk.len())) .map(|req_size| { @@ -162,7 +162,7 @@ async fn test_chunked_payload() { let re = Regex::new(r"size=(\d+)").unwrap(); let size: usize = match re.captures(&data) { Some(caps) => caps.get(1).unwrap().as_str().parse().unwrap(), - None => panic!(format!("Failed to find size in HTTP Response: {}", data)), + None => panic!("Failed to find size in HTTP Response: {}", data), }; size }; diff --git a/tests/test_httpserver.rs b/tests/test_httpserver.rs index b6c0528dd..aa2b2ca74 100644 --- a/tests/test_httpserver.rs +++ b/tests/test_httpserver.rs @@ -89,7 +89,7 @@ fn ssl_acceptor() -> SslAcceptorBuilder { builder.set_certificate(&cert).unwrap(); builder.set_private_key(&key).unwrap(); - Ok(builder) + builder } #[actix_rt::test] From fc31b091e4e00067c52aba2801d79c25bb93a2c9 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 8 Mar 2021 23:07:40 +0000 Subject: [PATCH 04/36] prepare http release 3.0.0-beta.4 --- Cargo.toml | 2 +- actix-http-test/Cargo.toml | 2 +- actix-http/CHANGES.md | 5 ++++- actix-http/Cargo.toml | 2 +- actix-http/README.md | 4 ++-- actix-multipart/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- awc/Cargo.toml | 4 ++-- 8 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bd758ab10..28bb432c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,7 +87,7 @@ actix-utils = "3.0.0-beta.2" actix-tls = { version = "3.0.0-beta.4", default-features = false, optional = true } actix-web-codegen = "0.5.0-beta.1" -actix-http = "3.0.0-beta.3" +actix-http = "3.0.0-beta.4" awc = { version = "3.0.0-beta.2", default-features = false } ahash = "0.7" diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index 900960215..fd3edd9b8 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -58,4 +58,4 @@ optional = true [dev-dependencies] actix-web = { version = "4.0.0-beta.3", default-features = false, features = ["cookies"] } -actix-http = "3.0.0-beta.3" +actix-http = "3.0.0-beta.4" diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 14218289d..a5e69eda3 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,13 +1,16 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 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] ### Removed -* re-export of `futures_channel::oneshot::Canceled` is removed from `error` mod. [#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 diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index c79ad11b2..d10e1ebdf 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "3.0.0-beta.3" +version = "3.0.0-beta.4" authors = ["Nikolay Kim "] description = "HTTP primitives for the Actix ecosystem" readme = "README.md" diff --git a/actix-http/README.md b/actix-http/README.md index 881fbc8c5..53fedd40e 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.3)](https://docs.rs/actix-http/3.0.0-beta.3) +[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.0.0-beta.4)](https://docs.rs/actix-http/3.0.0-beta.4) [![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.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.3/status.svg)](https://deps.rs/crate/actix-http/3.0.0-beta.3) +[![dependency status](https://deps.rs/crate/actix-http/3.0.0-beta.4/status.svg)](https://deps.rs/crate/actix-http/3.0.0-beta.4) [![Download](https://img.shields.io/crates/d/actix-http.svg)](https://crates.io/crates/actix-http) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index fcbadde36..34817e087 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -29,6 +29,6 @@ twoway = "0.2" [dev-dependencies] actix-rt = "2.1" -actix-http = "3.0.0-beta.3" +actix-http = "3.0.0-beta.4" tokio = { version = "1", features = ["sync"] } tokio-stream = "0.1" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index e0c4fc073..5ddbe53ce 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -18,7 +18,7 @@ path = "src/lib.rs" [dependencies] actix = { version = "0.11.0-beta.3", default-features = false } actix-codec = "0.4.0-beta.1" -actix-http = "3.0.0-beta.3" +actix-http = "3.0.0-beta.4" actix-web = { version = "4.0.0-beta.3", default-features = false } bytes = "1" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 761871ba2..166a08fd5 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -46,7 +46,7 @@ trust-dns = ["actix-http/trust-dns"] [dependencies] actix-codec = "0.4.0-beta.1" actix-service = "2.0.0-beta.4" -actix-http = "3.0.0-beta.3" +actix-http = "3.0.0-beta.4" actix-rt = { version = "2.1", default-features = false } base64 = "0.13" @@ -74,7 +74,7 @@ optional = true [dev-dependencies] actix-web = { version = "4.0.0-beta.3", features = ["openssl"] } -actix-http = { version = "3.0.0-beta.3", features = ["openssl"] } +actix-http = { version = "3.0.0-beta.4", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.2", features = ["openssl"] } actix-utils = "3.0.0-beta.1" actix-server = "2.0.0-beta.3" From 23b0e64199eaf4d10cdc1727c68d7ad3da298eb2 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 8 Mar 2021 23:15:53 +0000 Subject: [PATCH 05/36] prepare awc release 3.0.0-beta.3 --- Cargo.toml | 2 +- actix-http-test/Cargo.toml | 2 +- awc/CHANGES.md | 3 +++ awc/Cargo.toml | 2 +- awc/README.md | 4 ++-- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 28bb432c7..48606f6ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -88,7 +88,7 @@ actix-tls = { version = "3.0.0-beta.4", default-features = false, optional = tru actix-web-codegen = "0.5.0-beta.1" actix-http = "3.0.0-beta.4" -awc = { version = "3.0.0-beta.2", default-features = false } +awc = { version = "3.0.0-beta.3", default-features = false } ahash = "0.7" bytes = "1" diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index fd3edd9b8..ca51bd325 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -35,7 +35,7 @@ actix-tls = "3.0.0-beta.4" actix-utils = "3.0.0-beta.2" actix-rt = "2.1" actix-server = "2.0.0-beta.3" -awc = { version = "3.0.0-beta.2", default-features = false } +awc = { version = "3.0.0-beta.3", default-features = false } base64 = "0.13" bytes = "1" diff --git a/awc/CHANGES.md b/awc/CHANGES.md index b11d64628..bf7bffc49 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 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] diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 166a08fd5..c55465b5d 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "awc" -version = "3.0.0-beta.2" +version = "3.0.0-beta.3" authors = ["Nikolay Kim "] description = "Async HTTP and WebSocket client library built on the Actix ecosystem" readme = "README.md" diff --git a/awc/README.md b/awc/README.md index 1f6e3b8fb..ec3984d60 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.2)](https://docs.rs/awc/3.0.0-beta.2) +[![Documentation](https://docs.rs/awc/badge.svg?version=3.0.0-beta.3)](https://docs.rs/awc/3.0.0-beta.3) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/awc) -[![Dependency Status](https://deps.rs/crate/awc/3.0.0-beta.2/status.svg)](https://deps.rs/crate/awc/3.0.0-beta.2) +[![Dependency Status](https://deps.rs/crate/awc/3.0.0-beta.3/status.svg)](https://deps.rs/crate/awc/3.0.0-beta.3) [![Join the chat at https://gitter.im/actix/actix-web](https://badges.gitter.im/actix/actix-web.svg)](https://gitter.im/actix/actix-web?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ## Documentation & Resources From c4e565121569880e6e6b60e8d755ca3fa637f230 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Mon, 8 Mar 2021 23:49:12 +0000 Subject: [PATCH 06/36] update docs --- docs/graphs/web-focus.dot | 53 +++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/docs/graphs/web-focus.dot b/docs/graphs/web-focus.dot index ec0f7a946..2c6e2779b 100644 --- a/docs/graphs/web-focus.dot +++ b/docs/graphs/web-focus.dot @@ -1,35 +1,44 @@ digraph { subgraph cluster_web { - label="actix/actix-web" + label="actix/web" "awc" - "actix-web" - "actix-files" - "actix-http" - "actix-multipart" - "actix-web-actors" - "actix-web-codegen" - "actix-http-test" + "web" + "files" + "http" + "multipart" + "web-actors" + "web-codegen" + "http-test" + + { rank=same; "multipart" "web-actors" "http-test" }; + { rank=same; "files" "awc" "web" }; + { rank=same; "web-codegen" "http" }; } - "actix-web" -> { "actix-codec" "actix-service" "actix-utils" "actix-router" "actix-rt" "actix-server" "macros" "threadpool" "actix-tls" "actix-web-codegen" "actix-http" "awc" } - "awc" -> { "actix-codec" "actix-service" "actix-http" "actix-rt" } - "actix-web-actors" -> { "actix" "actix-web" "actix-http" "actix-codec" } - "actix-multipart" -> { "actix-web" "actix-service" "actix-utils" } - "actix-http" -> { "actix-service" "actix-codec" "actix-tls" "actix-utils" "actix-rt" "threadpool" } - "actix-http" -> { "actix-tls" }[color=blue] // optional - "actix-files" -> { "actix-web" } - "actix-http-test" -> { "actix-service" "actix-codec" "actix-tls" "actix-utils" "actix-rt" "actix-server" "awc" } + "web" -> { "codec" "service" "utils" "router" "rt" "server" "macros" "web-codegen" "http" "awc" } + "web" -> { "tls" }[color=blue] // optional + "awc" -> { "codec" "service" "http" "rt" } + "web-actors" -> { "actix" "web" "http" "codec" } + "multipart" -> { "web" "service" "utils" } + "http" -> { "service" "codec" "utils" "rt" } + "http" -> { "tls" }[color=blue] // optional + "files" -> { "web" } + "http-test" -> { "service" "codec" "utils" "rt" "server" "awc" } + "http-test" -> { "tls" }[color=blue] // optional // net - "actix-utils" -> { "actix-service" "actix-rt" "actix-codec" } - "actix-tracing" -> { "actix-service" } - "actix-tls" -> { "actix-service" "actix-codec" "actix-utils" } - "actix-server" -> { "actix-service" "actix-rt" "actix-codec" "actix-utils" } - "actix-rt" -> { "macros" "threadpool" } + "utils" -> { "service" "rt" "codec" } + "tracing" -> { "service" } + "tls" -> { "service" "codec" "utils" } + "server" -> { "service" "rt" "codec" "utils" } + "rt" -> { "macros" } + + { rank=same; "utils" "codec" }; + { rank=same; "rt" "macros" "service" "router" }; // actix - "actix" -> { "actix-rt" } + "actix" -> { "rt" } } From b7c406637d7566547891843f45fd57991336b31f Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 9 Mar 2021 23:27:38 +0000 Subject: [PATCH 07/36] prepare actix-web-codegen release 0.5.0-beta.2 --- Cargo.toml | 2 +- actix-web-codegen/CHANGES.md | 3 +++ actix-web-codegen/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 48606f6ed..d002ce676 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,7 +86,7 @@ actix-service = "2.0.0-beta.4" actix-utils = "3.0.0-beta.2" actix-tls = { version = "3.0.0-beta.4", default-features = false, optional = true } -actix-web-codegen = "0.5.0-beta.1" +actix-web-codegen = "0.5.0-beta.2" actix-http = "3.0.0-beta.4" awc = { version = "3.0.0-beta.3", default-features = false } diff --git a/actix-web-codegen/CHANGES.md b/actix-web-codegen/CHANGES.md index 7c6543d49..f2c9b44b5 100644 --- a/actix-web-codegen/CHANGES.md +++ b/actix-web-codegen/CHANGES.md @@ -1,6 +1,9 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 0.5.0-beta.2 - 2021-03-09 * Preserve doc comments when using route macros. [#2022] * Add `name` attribute to `route` macro. [#1934] diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index e43e91e22..e74e0ed85 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.1" +version = "0.5.0-beta.2" description = "Routing and runtime macros for Actix Web" readme = "README.md" homepage = "https://actix.rs" From 4b46351d36454224e5d33cde001114ec54c36aa2 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 9 Mar 2021 23:31:44 +0000 Subject: [PATCH 08/36] prepare actix-web release 4.0.0-beta.4 --- CHANGES.md | 7 +++++-- Cargo.toml | 2 +- README.md | 4 ++-- actix-files/Cargo.toml | 4 ++-- actix-http-test/Cargo.toml | 2 +- actix-multipart/Cargo.toml | 2 +- actix-web-actors/Cargo.toml | 2 +- actix-web-codegen/Cargo.toml | 2 +- actix-web-codegen/README.md | 4 ++-- awc/Cargo.toml | 2 +- 10 files changed, 17 insertions(+), 14 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 819237ab7..a6d4487dc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,10 +1,13 @@ # Changes ## Unreleased - 2021-xx-xx + + +## 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 behaviour of the `web::Json` extractor. [#2010] +* `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 diff --git a/Cargo.toml b/Cargo.toml index d002ce676..8477c8ede 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-web" -version = "4.0.0-beta.3" +version = "4.0.0-beta.4" authors = ["Nikolay Kim "] description = "Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust" readme = "README.md" diff --git a/README.md b/README.md index b3448140a..64fd7d08d 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.2)](https://docs.rs/actix-web/4.0.0-beta.2) +[![Documentation](https://docs.rs/actix-web/badge.svg?version=4.0.0-beta.4)](https://docs.rs/actix-web/4.0.0-beta.4) [![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.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.2/status.svg)](https://deps.rs/crate/actix-web/4.0.0-beta.2) +[![Dependency Status](https://deps.rs/crate/actix-web/4.0.0-beta.4/status.svg)](https://deps.rs/crate/actix-web/4.0.0-beta.4)
[![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 8f1a9ec5a..80b0310bc 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -17,7 +17,7 @@ name = "actix_files" path = "src/lib.rs" [dependencies] -actix-web = { version = "4.0.0-beta.3", default-features = false } +actix-web = { version = "4.0.0-beta.4", default-features = false } actix-service = "2.0.0-beta.4" askama_escape = "0.10" @@ -34,4 +34,4 @@ percent-encoding = "2.1" [dev-dependencies] actix-rt = "2.1" -actix-web = "4.0.0-beta.3" +actix-web = "4.0.0-beta.4" diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index ca51bd325..855f53fe9 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -57,5 +57,5 @@ features = ["vendored"] optional = true [dev-dependencies] -actix-web = { version = "4.0.0-beta.3", default-features = false, features = ["cookies"] } +actix-web = { version = "4.0.0-beta.4", default-features = false, features = ["cookies"] } actix-http = "3.0.0-beta.4" diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 34817e087..23c41a0f3 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -16,7 +16,7 @@ name = "actix_multipart" path = "src/lib.rs" [dependencies] -actix-web = { version = "4.0.0-beta.3", default-features = false } +actix-web = { version = "4.0.0-beta.4", default-features = false } actix-utils = "3.0.0-beta.2" bytes = "1" diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index 5ddbe53ce..e305cd750 100644 --- a/actix-web-actors/Cargo.toml +++ b/actix-web-actors/Cargo.toml @@ -19,7 +19,7 @@ path = "src/lib.rs" actix = { version = "0.11.0-beta.3", default-features = false } actix-codec = "0.4.0-beta.1" actix-http = "3.0.0-beta.4" -actix-web = { version = "4.0.0-beta.3", default-features = false } +actix-web = { version = "4.0.0-beta.4", default-features = false } bytes = "1" bytestring = "1" diff --git a/actix-web-codegen/Cargo.toml b/actix-web-codegen/Cargo.toml index e74e0ed85..d8a189565 100644 --- a/actix-web-codegen/Cargo.toml +++ b/actix-web-codegen/Cargo.toml @@ -20,7 +20,7 @@ proc-macro2 = "1" [dev-dependencies] actix-rt = "2.1" -actix-web = "4.0.0-beta.3" +actix-web = "4.0.0-beta.4" futures-util = { version = "0.3.7", default-features = false } trybuild = "1" rustversion = "1" diff --git a/actix-web-codegen/README.md b/actix-web-codegen/README.md index 5820bb443..9552d4b56 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.1)](https://docs.rs/actix-web-codegen/0.5.0-beta.1) +[![Documentation](https://docs.rs/actix-web-codegen/badge.svg?version=0.5.0-beta.2)](https://docs.rs/actix-web-codegen/0.5.0-beta.2) [![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.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.1/status.svg)](https://deps.rs/crate/actix-web-codegen/0.5.0-beta.1) +[![dependency status](https://deps.rs/crate/actix-web-codegen/0.5.0-beta.2/status.svg)](https://deps.rs/crate/actix-web-codegen/0.5.0-beta.2) [![Download](https://img.shields.io/crates/d/actix-web-codegen.svg)](https://crates.io/crates/actix-web-codegen) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) diff --git a/awc/Cargo.toml b/awc/Cargo.toml index c55465b5d..bca3ada4a 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -73,7 +73,7 @@ features = ["vendored"] optional = true [dev-dependencies] -actix-web = { version = "4.0.0-beta.3", features = ["openssl"] } +actix-web = { version = "4.0.0-beta.4", features = ["openssl"] } actix-http = { version = "3.0.0-beta.4", features = ["openssl"] } actix-http-test = { version = "3.0.0-beta.2", features = ["openssl"] } actix-utils = "3.0.0-beta.1" From 18c3783a1c0bd74cac3e764fb264c4bf53986895 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 9 Mar 2021 23:35:42 +0000 Subject: [PATCH 09/36] prepare actix-http-test release 3.0.0-beta.3 --- actix-http-test/CHANGES.md | 4 ++++ actix-http-test/Cargo.toml | 2 +- actix-http/Cargo.toml | 2 +- awc/Cargo.toml | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/actix-http-test/CHANGES.md b/actix-http-test/CHANGES.md index 2f47d700d..d6a2cdd9b 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.3 - 2021-03-09 +* No notable changes. + + ## 3.0.0-beta.2 - 2021-02-10 * No notable changes. diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index 855f53fe9..69b2a3335 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.2" +version = "3.0.0-beta.3" authors = ["Nikolay Kim "] description = "Various helpers for Actix applications to use during testing" readme = "README.md" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index d10e1ebdf..edcc7efd5 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -89,7 +89,7 @@ trust-dns-resolver = { version = "0.20.0", optional = true } [dev-dependencies] actix-server = "2.0.0-beta.3" -actix-http-test = { version = "3.0.0-beta.2", features = ["openssl"] } +actix-http-test = { version = "3.0.0-beta.3", features = ["openssl"] } actix-tls = { version = "3.0.0-beta.4", features = ["openssl"] } criterion = "0.3" env_logger = "0.8" diff --git a/awc/Cargo.toml b/awc/Cargo.toml index bca3ada4a..981b93a52 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -75,7 +75,7 @@ optional = true [dev-dependencies] actix-web = { version = "4.0.0-beta.4", features = ["openssl"] } actix-http = { version = "3.0.0-beta.4", features = ["openssl"] } -actix-http-test = { version = "3.0.0-beta.2", features = ["openssl"] } +actix-http-test = { version = "3.0.0-beta.3", features = ["openssl"] } actix-utils = "3.0.0-beta.1" actix-server = "2.0.0-beta.3" actix-tls = { version = "3.0.0-beta.4", features = ["openssl", "rustls"] } From 3451d6874f3efea0ae5dfcf90643c9450686093f Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 9 Mar 2021 23:39:40 +0000 Subject: [PATCH 10/36] prepare actix-files release 0.6.0-beta.3 --- actix-files/CHANGES.md | 4 ++++ actix-files/Cargo.toml | 2 +- actix-files/README.md | 4 ++-- actix-http-test/README.md | 4 ++-- actix-multipart/README.md | 1 - 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 6e2a241ac..c035d5afe 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 0.6.0-beta.3 - 2021-03-09 +* 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] diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index 80b0310bc..49cd6966c 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.6.0-beta.2" +version = "0.6.0-beta.3" authors = ["Nikolay Kim "] description = "Static file serving for Actix Web" readme = "README.md" diff --git a/actix-files/README.md b/actix-files/README.md index a4f0445aa..c7b7424ec 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.5.0)](https://docs.rs/actix-files/0.5.0) +[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.0-beta.3)](https://docs.rs/actix-files/0.6.0-beta.3) [![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html) ![License](https://img.shields.io/crates/l/actix-files.svg)
-[![dependency status](https://deps.rs/crate/actix-files/0.5.0/status.svg)](https://deps.rs/crate/actix-files/0.5.0) +[![dependency status](https://deps.rs/crate/actix-files/0.6.0-beta.3/status.svg)](https://deps.rs/crate/actix-files/0.6.0-beta.3) [![Download](https://img.shields.io/crates/d/actix-files.svg)](https://crates.io/crates/actix-files) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) diff --git a/actix-http-test/README.md b/actix-http-test/README.md index 66f15979d..8cec94808 100644 --- a/actix-http-test/README.md +++ b/actix-http-test/README.md @@ -3,9 +3,9 @@ > 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=2.1.0)](https://docs.rs/actix-http-test/2.1.0) +[![Documentation](https://docs.rs/actix-http-test/badge.svg?version=3.0.0-beta.3)](https://docs.rs/actix-http-test/3.0.0-beta.3) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http-test) -[![Dependency Status](https://deps.rs/crate/actix-http-test/2.1.0/status.svg)](https://deps.rs/crate/actix-http-test/2.1.0) +[![Dependency Status](https://deps.rs/crate/actix-http-test/3.0.0-beta.3/status.svg)](https://deps.rs/crate/actix-http-test/3.0.0-beta.3) [![Join the chat at https://gitter.im/actix/actix-web](https://badges.gitter.im/actix/actix-web.svg)](https://gitter.im/actix/actix-web?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ## Documentation & Resources diff --git a/actix-multipart/README.md b/actix-multipart/README.md index defcf7828..d39d8496d 100644 --- a/actix-multipart/README.md +++ b/actix-multipart/README.md @@ -9,7 +9,6 @@
[![dependency status](https://deps.rs/crate/actix-multipart/0.4.0-beta.2/status.svg)](https://deps.rs/crate/actix-multipart/0.4.0-beta.2) [![Download](https://img.shields.io/crates/d/actix-multipart.svg)](https://crates.io/crates/actix-multipart) -[![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ## Documentation & Resources From 5e9a3eb6ae801a8b1370d917926ab20df713d421 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 9 Mar 2021 23:40:50 +0000 Subject: [PATCH 11/36] prepare actix-multipart release 0.4.0-beta.3 --- 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 2142ebf4b..ce5bd57d7 100644 --- a/actix-multipart/CHANGES.md +++ b/actix-multipart/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased - 2021-xx-xx +## 0.4.0-beta.3 - 2021-03-09 +* No notable changes. + + ## 0.4.0-beta.2 - 2021-02-10 * No notable changes. diff --git a/actix-multipart/Cargo.toml b/actix-multipart/Cargo.toml index 23c41a0f3..9a3ea7bb5 100644 --- a/actix-multipart/Cargo.toml +++ b/actix-multipart/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-multipart" -version = "0.4.0-beta.2" +version = "0.4.0-beta.3" authors = ["Nikolay Kim "] description = "Multipart form support for Actix Web" readme = "README.md" diff --git a/actix-multipart/README.md b/actix-multipart/README.md index d39d8496d..e4a54ac47 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.2)](https://docs.rs/actix-multipart/0.4.0-beta.2) +[![Documentation](https://docs.rs/actix-multipart/badge.svg?version=0.4.0-beta.3)](https://docs.rs/actix-multipart/0.4.0-beta.3) [![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.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.2/status.svg)](https://deps.rs/crate/actix-multipart/0.4.0-beta.2) +[![dependency status](https://deps.rs/crate/actix-multipart/0.4.0-beta.3/status.svg)](https://deps.rs/crate/actix-multipart/0.4.0-beta.3) [![Download](https://img.shields.io/crates/d/actix-multipart.svg)](https://crates.io/crates/actix-multipart) ## Documentation & Resources From b62da7e86b97ebe3669d3ba4dbc0628a2c90efc7 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Tue, 9 Mar 2021 23:44:26 +0000 Subject: [PATCH 12/36] prepare actix-web-actors release 4.0.0-beta.3 --- actix-web-actors/CHANGES.md | 4 ++++ actix-web-actors/Cargo.toml | 2 +- actix-web-actors/README.md | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/actix-web-actors/CHANGES.md b/actix-web-actors/CHANGES.md index acd9ceada..80eef08c1 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.3 - 2021-03-09 +* No notable changes. + + ## 4.0.0-beta.2 - 2021-02-10 * No notable changes. diff --git a/actix-web-actors/Cargo.toml b/actix-web-actors/Cargo.toml index e305cd750..77663540c 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.2" +version = "4.0.0-beta.3" authors = ["Nikolay Kim "] description = "Actix actors support for Actix Web" readme = "README.md" diff --git a/actix-web-actors/README.md b/actix-web-actors/README.md index c9b588153..6d9d573d7 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=0.5.0)](https://docs.rs/actix-web-actors/0.5.0) +[![Documentation](https://docs.rs/actix-web-actors/badge.svg?version=4.0.0-beta.3)](https://docs.rs/actix-web-actors/4.0.0-beta.3) [![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html) ![License](https://img.shields.io/crates/l/actix-web-actors.svg)
-[![dependency status](https://deps.rs/crate/actix-web-actors/0.5.0/status.svg)](https://deps.rs/crate/actix-web-actors/0.5.0) +[![dependency status](https://deps.rs/crate/actix-web-actors/4.0.0-beta.3/status.svg)](https://deps.rs/crate/actix-web-actors/4.0.0-beta.3) [![Download](https://img.shields.io/crates/d/actix-web-actors.svg)](https://crates.io/crates/actix-web-actors) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) From d0c1f1a84cb0d1fdf86ccf715a30f7989c988a51 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Tue, 9 Mar 2021 17:31:50 -0800 Subject: [PATCH 13/36] remove actix_http::client::pool::Protocol (#2061) --- actix-http/src/client/connector.rs | 24 +++++------------------- actix-http/src/client/mod.rs | 2 +- actix-http/src/client/pool.rs | 23 ++++------------------- 3 files changed, 10 insertions(+), 39 deletions(-) diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 1a926fd6c..28c865ffa 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -20,8 +20,9 @@ use http::Uri; use super::config::ConnectorConfig; use super::connection::{Connection, EitherIoConnection}; use super::error::ConnectError; -use super::pool::{ConnectionPool, Protocol}; +use super::pool::ConnectionPool; use super::Connect; +use super::Protocol; #[cfg(feature = "openssl")] use actix_tls::connect::ssl::openssl::SslConnector as OpensslConnector; @@ -148,7 +149,7 @@ where + 'static, { /// Connection timeout, i.e. max time to connect to remote host including dns name resolution. - /// Set to 1 second by default. + /// Set to 5 second by default. pub fn timeout(mut self, timeout: Duration) -> Self { self.config.timeout = timeout; self @@ -162,6 +163,7 @@ where } #[cfg(feature = "rustls")] + /// Use custom `SslConnector` instance. pub fn rustls(mut self, connector: Arc) -> Self { self.ssl = SslConnector::Rustls(connector); self @@ -254,8 +256,7 @@ where /// its combinator chain. pub fn finish( self, - ) -> impl Service + Clone - { + ) -> impl Service { let local_address = self.config.local_address; let timeout = self.config.timeout; @@ -392,21 +393,6 @@ where tls_pool: Option>, } -impl Clone for InnerConnector -where - S1: Service + 'static, - S2: Service + 'static, - Io1: AsyncRead + AsyncWrite + Unpin + 'static, - Io2: AsyncRead + AsyncWrite + Unpin + 'static, -{ - fn clone(&self) -> Self { - InnerConnector { - tcp_pool: self.tcp_pool.clone(), - tls_pool: self.tls_pool.as_ref().cloned(), - } - } -} - impl Service for InnerConnector where S1: Service + 'static, diff --git a/actix-http/src/client/mod.rs b/actix-http/src/client/mod.rs index 5f5e57edb..c6f998c2a 100644 --- a/actix-http/src/client/mod.rs +++ b/actix-http/src/client/mod.rs @@ -17,7 +17,7 @@ pub use actix_tls::connect::{ pub use self::connection::Connection; pub use self::connector::Connector; pub use self::error::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError}; -pub use self::pool::Protocol; +pub use crate::Protocol; #[derive(Clone)] pub struct Connect { diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index 3800696fa..b5b4a30d3 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -25,13 +25,7 @@ use super::connection::{ConnectionType, H2Connection, IoConnection}; use super::error::ConnectError; use super::h2proto::handshake; use super::Connect; - -#[derive(Clone, Copy, PartialEq)] -/// Protocol version -pub enum Protocol { - Http1, - Http2, -} +use super::Protocol; #[derive(Hash, Eq, PartialEq, Clone, Debug)] pub(crate) struct Key { @@ -148,18 +142,6 @@ where } } -impl Clone for ConnectionPool -where - Io: AsyncWrite + Unpin + 'static, -{ - fn clone(&self) -> Self { - Self { - connector: self.connector.clone(), - inner: self.inner.clone(), - } - } -} - impl Service for ConnectionPool where S: Service + 'static, @@ -243,6 +225,9 @@ where None => { let (io, proto) = connector.call(req).await?; + // TODO: remove when http3 is added in support. + assert!(proto != Protocol::Http3); + if proto == Protocol::Http1 { Ok(IoConnection::new( ConnectionType::H1(io), From a2b0e866325b1492c2daa9fd0593ce5d91e40af8 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 10 Mar 2021 15:57:32 -0800 Subject: [PATCH 14/36] simplify connector generic type (#2063) --- actix-http/CHANGES.md | 4 ++++ actix-http/src/client/connector.rs | 22 +++++++++++----------- awc/src/builder.rs | 11 +++++------ awc/src/lib.rs | 1 - 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index a5e69eda3..e96c22274 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,6 +1,10 @@ # Changes ## Unreleased - 2021-xx-xx +### Chaged +* `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063] + +[#2063]: https://github.com/actix/actix-web/pull/2063 ## 3.0.0-beta.4 - 2021-03-08 diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 28c865ffa..ebdbbb6eb 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -1,7 +1,6 @@ use std::{ fmt, future::Future, - marker::PhantomData, net::IpAddr, pin::Pin, task::{Context, Poll}, @@ -15,6 +14,7 @@ use actix_tls::connect::{ new_connector, Connect as TcpConnect, Connection as TcpConnection, Resolver, }; use actix_utils::timeout::{TimeoutError, TimeoutService}; +use futures_core::ready; use http::Uri; use super::config::ConnectorConfig; @@ -54,18 +54,17 @@ type SslConnector = (); /// .timeout(Duration::from_secs(5)) /// .finish(); /// ``` -pub struct Connector { +pub struct Connector { connector: T, config: ConnectorConfig, #[allow(dead_code)] ssl: SslConnector, - _phantom: PhantomData, } pub trait Io: AsyncRead + AsyncWrite + Unpin {} impl Io for T {} -impl Connector<(), ()> { +impl Connector<()> { #[allow(clippy::new_ret_no_self, clippy::let_unit_value)] pub fn new() -> Connector< impl Service< @@ -73,13 +72,11 @@ impl Connector<(), ()> { Response = TcpConnection, Error = actix_tls::connect::ConnectError, > + Clone, - TcpStream, > { Connector { ssl: Self::build_ssl(vec![b"h2".to_vec(), b"http/1.1".to_vec()]), connector: new_connector(resolver::resolver()), config: ConnectorConfig::default(), - _phantom: PhantomData, } } @@ -118,9 +115,9 @@ impl Connector<(), ()> { fn build_ssl(_: Vec>) -> SslConnector {} } -impl Connector { +impl Connector { /// Use custom connector. - pub fn connector(self, connector: T1) -> Connector + pub fn connector(self, connector: T1) -> Connector where U1: AsyncRead + AsyncWrite + Unpin + fmt::Debug, T1: Service< @@ -133,12 +130,11 @@ impl Connector { connector, config: self.config, ssl: self.ssl, - _phantom: PhantomData, } } } -impl Connector +impl Connector where U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, T: Service< @@ -405,7 +401,11 @@ where type Future = InnerConnectorResponse; fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { - self.tcp_pool.poll_ready(cx) + ready!(self.tcp_pool.poll_ready(cx))?; + if let Some(ref tls_pool) = self.tls_pool { + ready!(tls_pool.poll_ready(cx))?; + } + Poll::Ready(Ok(())) } fn call(&self, req: Connect) -> Self::Future { diff --git a/awc/src/builder.rs b/awc/src/builder.rs index 72a0f4f04..33f43ab93 100644 --- a/awc/src/builder.rs +++ b/awc/src/builder.rs @@ -21,14 +21,14 @@ use crate::{Client, ClientConfig, ConnectRequest, ConnectResponse, ConnectorServ /// /// This type can be used to construct an instance of `Client` through a /// builder-like pattern. -pub struct ClientBuilder { +pub struct ClientBuilder { default_headers: bool, max_http_version: Option, stream_window_size: Option, conn_window_size: Option, headers: HeaderMap, timeout: Option, - connector: Connector, + connector: Connector, middleware: M, local_address: Option, max_redirects: u8, @@ -42,7 +42,6 @@ impl ClientBuilder { Response = TcpConnection, Error = TcpConnectError, > + Clone, - TcpStream, (), > { ClientBuilder { @@ -60,7 +59,7 @@ impl ClientBuilder { } } -impl ClientBuilder +impl ClientBuilder where S: Service, Response = TcpConnection, Error = TcpConnectError> + Clone @@ -68,7 +67,7 @@ where Io: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, { /// Use custom connector service. - pub fn connector(self, connector: Connector) -> ClientBuilder + pub fn connector(self, connector: Connector) -> ClientBuilder where S1: Service< TcpConnect, @@ -213,7 +212,7 @@ where pub fn wrap( self, mw: M1, - ) -> ClientBuilder> + ) -> ClientBuilder> where M: Transform, M1: Transform, diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 4cd1d5bb2..77e08fbbd 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -175,7 +175,6 @@ impl Client { Response = TcpConnection, Error = TcpConnectError, > + Clone, - TcpStream, > { ClientBuilder::new() } From 909ef0344b0fb5a30ead66420a89358e867b1933 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 11 Mar 2021 00:43:03 +0000 Subject: [PATCH 15/36] document client mod removal closes #2064 --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index a6d4487dc..adaa273e8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,8 @@ # Changes ## Unreleased - 2021-xx-xx +* The `client` mod was removed. Clients should now use `awc` directly. + [871ca5e4](https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7) ## 4.0.0-beta.4 - 2021-03-09 From 22dcc311937967b9da972b6304cb97d5684d2cae Mon Sep 17 00:00:00 2001 From: Ilya Averyanov Date: Thu, 11 Mar 2021 17:12:42 +0300 Subject: [PATCH 16/36] Fix logger middleware properly escape %% (#2067) --- CHANGES.md | 6 ++++++ src/middleware/logger.rs | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index adaa273e8..ef9ee900a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,9 +1,15 @@ # Changes ## Unreleased - 2021-xx-xx +### Fixed +* Double ampersand in Logger format is escaped correctly. [#2067] + +### Removed * The `client` mod was removed. Clients should now use `awc` directly. [871ca5e4](https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7) +[#2067]: https://github.com/actix/actix-web/pull/2067 + ## 4.0.0-beta.4 - 2021-03-09 ### Changed diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index 5b5b5577c..cedf51197 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -363,7 +363,7 @@ impl Format { /// Returns `None` if the format string syntax is incorrect. pub fn new(s: &str) -> Format { log::trace!("Access log format: {}", s); - let fmt = Regex::new(r"%(\{([A-Za-z0-9\-_]+)\}([aioe]|xi)|[atPrUsbTD]?)").unwrap(); + let fmt = Regex::new(r"%(\{([A-Za-z0-9\-_]+)\}([aioe]|xi)|[%atPrUsbTD]?)").unwrap(); let mut idx = 0; let mut results = Vec::new(); @@ -639,6 +639,38 @@ mod tests { let _res = srv.call(req).await.unwrap(); } + #[actix_rt::test] + async fn test_escape_percent() { + let mut format = Format::new("%%{r}a"); + + let req = TestRequest::default() + .insert_header(( + header::FORWARDED, + header::HeaderValue::from_static("for=192.0.2.60;proto=http;by=203.0.113.43"), + )) + .to_srv_request(); + + let now = OffsetDateTime::now_utc(); + for unit in &mut format.0 { + unit.render_request(now, &req); + } + + let resp = HttpResponse::build(StatusCode::OK).force_close().finish(); + for unit in &mut format.0 { + unit.render_response(&resp); + } + + let entry_time = OffsetDateTime::now_utc(); + let render = |fmt: &mut fmt::Formatter<'_>| { + for unit in &format.0 { + unit.render(fmt, 1024, entry_time)?; + } + Ok(()) + }; + let s = format!("{}", FormatDisplay(&render)); + assert_eq!(s, "%{r}a"); + } + #[actix_rt::test] async fn test_url_path() { let mut format = Format::new("%T %U"); From 515d0e3fb45d2936fad00daf0ab709ab2cc62103 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sat, 13 Mar 2021 14:20:18 -0800 Subject: [PATCH 17/36] change behavior of default upgrade handler (#2071) --- actix-http/src/h1/dispatcher.rs | 39 ++++++++++++++++++-------- actix-http/src/h1/upgrade.rs | 8 +++--- src/server.rs | 49 +++++++++++++++++---------------- 3 files changed, 57 insertions(+), 39 deletions(-) diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index 6df579c0a..e5989e5ee 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -662,7 +662,7 @@ where // got timeout during shutdown, drop connection if this.flags.contains(Flags::SHUTDOWN) { return Err(DispatchError::DisconnectTimeout); - // exceed deadline. check for any outstanding tasks + // exceed deadline. check for any outstanding tasks } else if timer.deadline() >= *this.ka_expire { // have no task at hand. if this.state.is_empty() && this.write_buf.is_empty() { @@ -695,15 +695,15 @@ where this.flags.insert(Flags::STARTED | Flags::SHUTDOWN); this.state.set(State::None); } - // still have unfinished task. try to reset and register keep-alive. + // still have unfinished task. try to reset and register keep-alive. } else if let Some(deadline) = this.codec.config().keep_alive_expire() { timer.as_mut().reset(deadline); let _ = timer.poll(cx); } - // timer resolved but still have not met the keep-alive expire deadline. - // reset and register for later wakeup. + // timer resolved but still have not met the keep-alive expire deadline. + // reset and register for later wakeup. } else { timer.as_mut().reset(*this.ka_expire); let _ = timer.poll(cx); @@ -951,14 +951,15 @@ mod tests { use std::str; use actix_service::fn_service; - use futures_util::future::{lazy, ready}; + use futures_util::future::{lazy, ready, Ready}; use super::*; - use crate::test::TestBuffer; - use crate::{error::Error, KeepAlive}; use crate::{ + error::Error, h1::{ExpectHandler, UpgradeHandler}, - test::TestSeqBuffer, + http::Method, + test::{TestBuffer, TestSeqBuffer}, + HttpMessage, KeepAlive, }; fn find_slice(haystack: &[u8], needle: &[u8], from: usize) -> Option { @@ -1282,14 +1283,30 @@ mod tests { #[actix_rt::test] async fn test_upgrade() { + struct TestUpgrade; + + impl Service<(Request, Framed)> for TestUpgrade { + type Response = (); + type Error = Error; + type Future = Ready>; + + actix_service::always_ready!(); + + fn call(&self, (req, _framed): (Request, Framed)) -> Self::Future { + assert_eq!(req.method(), Method::GET); + assert!(req.upgrade()); + assert_eq!(req.headers().get("upgrade").unwrap(), "websocket"); + ready(Ok(())) + } + } + lazy(|cx| { let mut buf = TestSeqBuffer::empty(); let cfg = ServiceConfig::new(KeepAlive::Disabled, 0, 0, false, None); - let services = - HttpFlow::new(ok_service(), ExpectHandler, Some(UpgradeHandler)); + let services = HttpFlow::new(ok_service(), ExpectHandler, Some(TestUpgrade)); - let h1 = Dispatcher::<_, _, _, _, UpgradeHandler>::new( + let h1 = Dispatcher::<_, _, _, _, TestUpgrade>::new( buf.clone(), cfg, services, diff --git a/actix-http/src/h1/upgrade.rs b/actix-http/src/h1/upgrade.rs index 5e24d84e3..fbfbc83c1 100644 --- a/actix-http/src/h1/upgrade.rs +++ b/actix-http/src/h1/upgrade.rs @@ -2,7 +2,7 @@ use std::task::Poll; use actix_codec::Framed; use actix_service::{Service, ServiceFactory}; -use futures_util::future::{ready, Ready}; +use futures_core::future::LocalBoxFuture; use crate::error::Error; use crate::h1::Codec; @@ -16,7 +16,7 @@ impl ServiceFactory<(Request, Framed)> for UpgradeHandler { type Config = (); type Service = UpgradeHandler; type InitError = Error; - type Future = Ready>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: ()) -> Self::Future { unimplemented!() @@ -26,11 +26,11 @@ impl ServiceFactory<(Request, Framed)> for UpgradeHandler { impl Service<(Request, Framed)> for UpgradeHandler { type Response = (); type Error = Error; - type Future = Ready>; + type Future = LocalBoxFuture<'static, Result>; actix_service::always_ready!(); fn call(&self, _: (Request, Framed)) -> Self::Future { - ready(Ok(())) + unimplemented!() } } diff --git a/src/server.rs b/src/server.rs index d69d6570d..99355593a 100644 --- a/src/server.rs +++ b/src/server.rs @@ -12,13 +12,6 @@ use actix_http::{ use actix_server::{Server, ServerBuilder}; use actix_service::{map_config, IntoServiceFactory, Service, ServiceFactory}; -#[cfg(unix)] -use actix_http::Protocol; -#[cfg(unix)] -use actix_service::pipeline_factory; -#[cfg(unix)] -use futures_util::future::ok; - #[cfg(feature = "openssl")] use actix_tls::accept::openssl::{AlpnError, SslAcceptor, SslAcceptorBuilder}; #[cfg(feature = "rustls")] @@ -489,7 +482,9 @@ where #[cfg(unix)] /// Start listening for unix domain (UDS) connections on existing listener. pub fn listen_uds(mut self, lst: std::os::unix::net::UnixListener) -> io::Result { + use actix_http::Protocol; use actix_rt::net::UnixStream; + use actix_service::pipeline_factory; let cfg = self.config.clone(); let factory = self.factory.clone(); @@ -511,19 +506,22 @@ where c.host.clone().unwrap_or_else(|| format!("{}", socket_addr)), ); - pipeline_factory(|io: UnixStream| ok((io, Protocol::Http1, None))).and_then({ - let svc = HttpService::build() - .keep_alive(c.keep_alive) - .client_timeout(c.client_timeout); + pipeline_factory(|io: UnixStream| async { Ok((io, Protocol::Http1, None)) }) + .and_then({ + let svc = HttpService::build() + .keep_alive(c.keep_alive) + .client_timeout(c.client_timeout); - let svc = if let Some(handler) = on_connect_fn.clone() { - svc.on_connect_ext(move |io: &_, ext: _| (&*handler)(io as &dyn Any, ext)) - } else { - svc - }; + let svc = if let Some(handler) = on_connect_fn.clone() { + svc.on_connect_ext(move |io: &_, ext: _| { + (&*handler)(io as &dyn Any, ext) + }) + } else { + svc + }; - svc.finish(map_config(factory(), move |_| config.clone())) - }) + svc.finish(map_config(factory(), move |_| config.clone())) + }) })?; Ok(self) } @@ -534,7 +532,9 @@ where where A: AsRef, { + use actix_http::Protocol; use actix_rt::net::UnixStream; + use actix_service::pipeline_factory; let cfg = self.config.clone(); let factory = self.factory.clone(); @@ -555,12 +555,13 @@ where socket_addr, c.host.clone().unwrap_or_else(|| format!("{}", socket_addr)), ); - pipeline_factory(|io: UnixStream| ok((io, Protocol::Http1, None))).and_then( - HttpService::build() - .keep_alive(c.keep_alive) - .client_timeout(c.client_timeout) - .finish(map_config(factory(), move |_| config.clone())), - ) + pipeline_factory(|io: UnixStream| async { Ok((io, Protocol::Http1, None)) }) + .and_then( + HttpService::build() + .keep_alive(c.keep_alive) + .client_timeout(c.client_timeout) + .finish(map_config(factory(), move |_| config.clone())), + ) }, )?; Ok(self) From a55e87faaa6ffe7b3f93aea082a783c6a64af9f7 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Sun, 14 Mar 2021 19:33:51 -0700 Subject: [PATCH 18/36] refactor actix_http::helpers to generic over bufmut trait (#2069) --- actix-http/src/helpers.rs | 41 ++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/actix-http/src/helpers.rs b/actix-http/src/helpers.rs index 13195f7db..74188717d 100644 --- a/actix-http/src/helpers.rs +++ b/actix-http/src/helpers.rs @@ -1,15 +1,15 @@ use std::io; -use bytes::{BufMut, BytesMut}; +use bytes::BufMut; use http::Version; const DIGITS_START: u8 = b'0'; -pub(crate) fn write_status_line(version: Version, n: u16, bytes: &mut BytesMut) { +pub(crate) fn write_status_line(version: Version, n: u16, buf: &mut B) { match version { - Version::HTTP_11 => bytes.put_slice(b"HTTP/1.1 "), - Version::HTTP_10 => bytes.put_slice(b"HTTP/1.0 "), - Version::HTTP_09 => bytes.put_slice(b"HTTP/0.9 "), + Version::HTTP_11 => buf.put_slice(b"HTTP/1.1 "), + Version::HTTP_10 => buf.put_slice(b"HTTP/1.0 "), + Version::HTTP_09 => buf.put_slice(b"HTTP/0.9 "), _ => { // other HTTP version handlers do not use this method } @@ -19,33 +19,36 @@ pub(crate) fn write_status_line(version: Version, n: u16, bytes: &mut BytesMut) let d10 = ((n / 10) % 10) as u8; let d1 = (n % 10) as u8; - bytes.put_u8(DIGITS_START + d100); - bytes.put_u8(DIGITS_START + d10); - bytes.put_u8(DIGITS_START + d1); + buf.put_u8(DIGITS_START + d100); + buf.put_u8(DIGITS_START + d10); + buf.put_u8(DIGITS_START + d1); // trailing space before reason - bytes.put_u8(b' '); + buf.put_u8(b' '); } /// NOTE: bytes object has to contain enough space -pub fn write_content_length(n: u64, bytes: &mut BytesMut) { +pub fn write_content_length(n: u64, buf: &mut B) { if n == 0 { - bytes.put_slice(b"\r\ncontent-length: 0\r\n"); + buf.put_slice(b"\r\ncontent-length: 0\r\n"); return; } - let mut buf = itoa::Buffer::new(); + let mut buffer = itoa::Buffer::new(); - bytes.put_slice(b"\r\ncontent-length: "); - bytes.put_slice(buf.format(n).as_bytes()); - bytes.put_slice(b"\r\n"); + buf.put_slice(b"\r\ncontent-length: "); + buf.put_slice(buffer.format(n).as_bytes()); + buf.put_slice(b"\r\n"); } -pub(crate) struct Writer<'a>(pub &'a mut BytesMut); +pub(crate) struct Writer<'a, B>(pub &'a mut B); -impl<'a> io::Write for Writer<'a> { +impl<'a, B> io::Write for Writer<'a, B> +where + B: BufMut, +{ fn write(&mut self, buf: &[u8]) -> io::Result { - self.0.extend_from_slice(buf); + self.0.put_slice(buf); Ok(buf.len()) } @@ -58,6 +61,8 @@ impl<'a> io::Write for Writer<'a> { mod tests { use std::str::from_utf8; + use bytes::BytesMut; + use super::*; #[test] From d93314a683d1a34826b23a3666e433283ffed850 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Mon, 15 Mar 2021 03:59:42 -0700 Subject: [PATCH 19/36] fix awc readme example (#2076) --- awc/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awc/README.md b/awc/README.md index ec3984d60..2e7dad320 100644 --- a/awc/README.md +++ b/awc/README.md @@ -27,7 +27,7 @@ fn main() { let res = client .get("http://www.rust-lang.org") // <- Create request builder - .header("User-Agent", "Actix-web") + .insert_header(("User-Agent", "Actix-web")) .send() // <- Send http request .await; From 69dd1a9bd643d8eb4f2689b6332eb16de4e06e90 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Mon, 15 Mar 2021 19:56:23 -0700 Subject: [PATCH 20/36] Remove ConnectionLifetime trait. Simplify Acquired handling (#2072) --- actix-http/src/client/connection.rs | 26 +------ actix-http/src/client/h1proto.rs | 117 +++++++++++++--------------- actix-http/src/client/h2proto.rs | 22 +++--- actix-http/src/client/pool.rs | 18 ++--- actix-http/src/h1/expect.rs | 2 - actix-http/src/h1/upgrade.rs | 2 - src/app_service.rs | 1 - src/resource.rs | 1 - src/scope.rs | 1 - 9 files changed, 76 insertions(+), 114 deletions(-) diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index 97ecd0515..9fb5c58cc 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -94,14 +94,6 @@ pub trait Connection { >; } -pub(crate) trait ConnectionLifetime: AsyncRead + AsyncWrite + 'static { - /// Close connection - fn close(self: Pin<&mut Self>); - - /// Release connection to the connection pool - fn release(self: Pin<&mut Self>); -} - #[doc(hidden)] /// HTTP client connection pub struct IoConnection @@ -110,7 +102,7 @@ where { io: Option>, created: time::Instant, - pool: Option>, + pool: Acquired, } impl fmt::Debug for IoConnection @@ -130,7 +122,7 @@ impl IoConnection { pub(crate) fn new( io: ConnectionType, created: time::Instant, - pool: Option>, + pool: Acquired, ) -> Self { IoConnection { pool, @@ -139,13 +131,9 @@ impl IoConnection { } } - pub(crate) fn into_inner(self) -> (ConnectionType, time::Instant) { - (self.io.unwrap(), self.created) - } - #[cfg(test)] pub(crate) fn into_parts(self) -> (ConnectionType, time::Instant, Acquired) { - (self.io.unwrap(), self.created, self.pool.unwrap()) + (self.io.unwrap(), self.created, self.pool) } async fn send_request>( @@ -173,13 +161,7 @@ impl IoConnection { match self.io.take().unwrap() { ConnectionType::H1(io) => h1proto::open_tunnel(io, head.into()).await, ConnectionType::H2(io) => { - if let Some(mut pool) = self.pool.take() { - pool.release(IoConnection::new( - ConnectionType::H2(io), - self.created, - None, - )); - } + self.pool.release(ConnectionType::H2(io), self.created); Err(SendRequestError::TunnelNotSupported) } } diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index d2db18cec..cd0b45734 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -7,7 +7,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed, ReadBuf}; use bytes::buf::BufMut; use bytes::{Bytes, BytesMut}; use futures_core::Stream; -use futures_util::{future::poll_fn, SinkExt, StreamExt}; +use futures_util::{future::poll_fn, SinkExt}; use crate::error::PayloadError; use crate::h1; @@ -19,7 +19,7 @@ use crate::http::{ use crate::message::{RequestHeadType, ResponseHead}; use crate::payload::{Payload, PayloadStream}; -use super::connection::{ConnectionLifetime, ConnectionType, IoConnection}; +use super::connection::ConnectionType; use super::error::{ConnectError, SendRequestError}; use super::pool::Acquired; use crate::body::{BodySize, MessageBody}; @@ -29,7 +29,7 @@ pub(crate) async fn send_request( mut head: RequestHeadType, body: B, created: time::Instant, - pool: Option>, + acquired: Acquired, ) -> Result<(ResponseHead, Payload), SendRequestError> where T: AsyncRead + AsyncWrite + Unpin + 'static, @@ -42,9 +42,9 @@ where if let Some(host) = head.as_ref().uri.host() { let mut wrt = BytesMut::with_capacity(host.len() + 5).writer(); - let _ = match head.as_ref().uri.port_u16() { - None | Some(80) | Some(443) => write!(wrt, "{}", host), - Some(port) => write!(wrt, "{}:{}", host, port), + match head.as_ref().uri.port_u16() { + None | Some(80) | Some(443) => write!(wrt, "{}", host)?, + Some(port) => write!(wrt, "{}:{}", host, port)?, }; match wrt.get_mut().split().freeze().try_into_value() { @@ -64,7 +64,7 @@ where let io = H1Connection { created, - pool, + acquired, io: Some(io), }; @@ -77,10 +77,8 @@ where let is_expect = if head.as_ref().headers.contains_key(EXPECT) { match body.size() { BodySize::None | BodySize::Empty | BodySize::Sized(0) => { - let pin_framed = Pin::new(&mut framed); - - let force_close = !pin_framed.codec_ref().keepalive(); - release_connection(pin_framed, force_close); + let keep_alive = framed.codec_ref().keepalive(); + framed.io_mut().on_release(keep_alive); // TODO: use a new variant or a new type better describing error violate // `Requirements for clients` session of above RFC @@ -128,8 +126,9 @@ where match pin_framed.codec_ref().message_type() { h1::MessageType::None => { - let force_close = !pin_framed.codec_ref().keepalive(); - release_connection(pin_framed, force_close); + let keep_alive = pin_framed.codec_ref().keepalive(); + pin_framed.io_mut().on_release(keep_alive); + Ok((head, Payload::None)) } _ => { @@ -151,12 +150,11 @@ where framed.send((head, BodySize::None).into()).await?; // read response - if let (Some(result), framed) = framed.into_future().await { - let head = result.map_err(SendRequestError::from)?; - Ok((head, framed)) - } else { - Err(SendRequestError::from(ConnectError::Disconnected)) - } + let head = poll_fn(|cx| Pin::new(&mut framed).poll_next(cx)) + .await + .ok_or(ConnectError::Disconnected)??; + + Ok((head, framed)) } /// send request body to the peer @@ -165,7 +163,7 @@ pub(crate) async fn send_body( mut framed: Pin<&mut Framed>, ) -> Result<(), SendRequestError> where - T: ConnectionLifetime + Unpin, + T: AsyncRead + AsyncWrite + Unpin + 'static, B: MessageBody, { actix_rt::pin!(body); @@ -200,7 +198,7 @@ where } } - SinkExt::flush(Pin::into_inner(framed)).await?; + SinkExt::flush(framed.get_mut()).await?; Ok(()) } @@ -208,41 +206,37 @@ where /// HTTP client connection pub struct H1Connection where - T: AsyncWrite + Unpin + 'static, + T: AsyncRead + AsyncWrite + Unpin + 'static, { /// T should be `Unpin` io: Option, created: time::Instant, - pool: Option>, + acquired: Acquired, } -impl ConnectionLifetime for H1Connection +impl H1Connection where T: AsyncRead + AsyncWrite + Unpin + 'static, { + fn on_release(&mut self, keep_alive: bool) { + if keep_alive { + self.release(); + } else { + self.close(); + } + } + /// Close connection - fn close(mut self: Pin<&mut Self>) { - if let Some(mut pool) = self.pool.take() { - if let Some(io) = self.io.take() { - pool.close(IoConnection::new( - ConnectionType::H1(io), - self.created, - None, - )); - } + fn close(&mut self) { + if let Some(io) = self.io.take() { + self.acquired.close(ConnectionType::H1(io)); } } /// Release this connection to the connection pool - fn release(mut self: Pin<&mut Self>) { - if let Some(mut pool) = self.pool.take() { - if let Some(io) = self.io.take() { - pool.release(IoConnection::new( - ConnectionType::H1(io), - self.created, - None, - )); - } + fn release(&mut self) { + if let Some(io) = self.io.take() { + self.acquired.release(ConnectionType::H1(io), self.created); } } } @@ -282,13 +276,19 @@ impl AsyncWrite for H1Connection } #[pin_project::pin_project] -pub(crate) struct PlStream { +pub(crate) struct PlStream +where + Io: AsyncRead + AsyncWrite + Unpin + 'static, +{ #[pin] - framed: Option>, + framed: Option, h1::ClientPayloadCodec>>, } -impl PlStream { - fn new(framed: Framed) -> Self { +impl PlStream +where + Io: AsyncRead + AsyncWrite + Unpin + 'static, +{ + fn new(framed: Framed, h1::ClientCodec>) -> Self { let framed = framed.into_map_codec(|codec| codec.into_payload_codec()); PlStream { @@ -297,24 +297,26 @@ impl PlStream { } } -impl Stream for PlStream { +impl Stream for PlStream +where + Io: AsyncRead + AsyncWrite + Unpin + 'static, +{ type Item = Result; fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - let mut this = self.project(); + let mut framed = self.project().framed.as_pin_mut().unwrap(); - match this.framed.as_mut().as_pin_mut().unwrap().next_item(cx)? { + match framed.as_mut().next_item(cx)? { Poll::Pending => Poll::Pending, Poll::Ready(Some(chunk)) => { if let Some(chunk) = chunk { Poll::Ready(Some(Ok(chunk))) } else { - let framed = this.framed.as_mut().as_pin_mut().unwrap(); - let force_close = !framed.codec_ref().keepalive(); - release_connection(framed, force_close); + let keep_alive = framed.codec_ref().keepalive(); + framed.io_mut().on_release(keep_alive); Poll::Ready(None) } } @@ -322,14 +324,3 @@ impl Stream for PlStream { } } } - -fn release_connection(framed: Pin<&mut Framed>, force_close: bool) -where - T: ConnectionLifetime, -{ - if !force_close && framed.is_read_buf_empty() && framed.is_write_buf_empty() { - framed.io_pin().release() - } else { - framed.io_pin().close() - } -} diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index 7292972de..82e81c7ff 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -17,7 +17,7 @@ use crate::message::{RequestHeadType, ResponseHead}; use crate::payload::Payload; use super::config::ConnectorConfig; -use super::connection::{ConnectionType, IoConnection}; +use super::connection::ConnectionType; use super::error::SendRequestError; use super::pool::Acquired; use crate::client::connection::H2Connection; @@ -27,7 +27,7 @@ pub(crate) async fn send_request( head: RequestHeadType, body: B, created: time::Instant, - pool: Option>, + acquired: Acquired, ) -> Result<(ResponseHead, Payload), SendRequestError> where T: AsyncRead + AsyncWrite + Unpin + 'static, @@ -103,13 +103,13 @@ where let res = poll_fn(|cx| io.poll_ready(cx)).await; if let Err(e) = res { - release(io, pool, created, e.is_io()); + release(io, acquired, created, e.is_io()); return Err(SendRequestError::from(e)); } let resp = match io.send_request(req, eof) { Ok((fut, send)) => { - release(io, pool, created, false); + release(io, acquired, created, false); if !eof { send_body(body, send).await?; @@ -117,7 +117,7 @@ where fut.await.map_err(SendRequestError::from)? } Err(e) => { - release(io, pool, created, e.is_io()); + release(io, acquired, created, e.is_io()); return Err(e.into()); } }; @@ -181,16 +181,14 @@ async fn send_body( /// release SendRequest object fn release( io: H2Connection, - pool: Option>, + acquired: Acquired, created: time::Instant, close: bool, ) { - if let Some(mut pool) = pool { - if close { - pool.close(IoConnection::new(ConnectionType::H2(io), created, None)); - } else { - pool.release(IoConnection::new(ConnectionType::H2(io), created, None)); - } + if close { + acquired.close(ConnectionType::H2(io)); + } else { + acquired.release(ConnectionType::H2(io), created); } } diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index b5b4a30d3..79adcf3e2 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -217,7 +217,7 @@ where // construct acquired. It's used to put Io type back to pool/ close the Io type. // permit is carried with the whole lifecycle of Acquired. - let acquired = Some(Acquired { key, inner, permit }); + let acquired = Acquired { key, inner, permit }; // match the connection and spawn new one if did not get anything. match conn { @@ -235,7 +235,7 @@ where acquired, )) } else { - let config = &acquired.as_ref().unwrap().inner.config; + let config = &acquired.inner.config; let (sender, connection) = handshake(io, config).await?; Ok(IoConnection::new( ConnectionType::H2(H2Connection::new(sender, connection)), @@ -346,14 +346,12 @@ where Io: AsyncRead + AsyncWrite + Unpin + 'static, { /// Close the IO. - pub(crate) fn close(&mut self, conn: IoConnection) { - let (conn, _) = conn.into_inner(); + pub(crate) fn close(&self, conn: ConnectionType) { self.inner.close(conn); } /// Release IO back into pool. - pub(crate) fn release(&mut self, conn: IoConnection) { - let (io, created) = conn.into_inner(); + pub(crate) fn release(&self, conn: ConnectionType, created: Instant) { let Acquired { key, inner, .. } = self; inner @@ -362,12 +360,12 @@ where .entry(key.clone()) .or_insert_with(VecDeque::new) .push_back(PooledConnection { - conn: io, + conn, created, used: Instant::now(), }); - let _ = &mut self.permit; + let _ = &self.permit; } } @@ -447,8 +445,8 @@ mod test { where T: AsyncRead + AsyncWrite + Unpin + 'static, { - let (conn, created, mut acquired) = conn.into_parts(); - acquired.release(IoConnection::new(conn, created, None)); + let (conn, created, acquired) = conn.into_parts(); + acquired.release(conn, created); } #[actix_rt::test] diff --git a/actix-http/src/h1/expect.rs b/actix-http/src/h1/expect.rs index 65856edf6..5015069bb 100644 --- a/actix-http/src/h1/expect.rs +++ b/actix-http/src/h1/expect.rs @@ -1,5 +1,3 @@ -use std::task::Poll; - use actix_service::{Service, ServiceFactory}; use futures_util::future::{ready, Ready}; diff --git a/actix-http/src/h1/upgrade.rs b/actix-http/src/h1/upgrade.rs index fbfbc83c1..e57ea8ae9 100644 --- a/actix-http/src/h1/upgrade.rs +++ b/actix-http/src/h1/upgrade.rs @@ -1,5 +1,3 @@ -use std::task::Poll; - use actix_codec::Framed; use actix_service::{Service, ServiceFactory}; use futures_core::future::LocalBoxFuture; diff --git a/src/app_service.rs b/src/app_service.rs index 9b4ae3354..be4ccf22f 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -1,6 +1,5 @@ use std::cell::RefCell; use std::rc::Rc; -use std::task::Poll; use actix_http::{Extensions, Request, Response}; use actix_router::{Path, ResourceDef, Router, Url}; diff --git a/src/resource.rs b/src/resource.rs index 944beeefa..1a5619de6 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -2,7 +2,6 @@ use std::cell::RefCell; use std::fmt; use std::future::Future; use std::rc::Rc; -use std::task::Poll; use actix_http::{Error, Extensions, Response}; use actix_router::IntoPattern; diff --git a/src/scope.rs b/src/scope.rs index dd02501b0..e96f5b9e8 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -2,7 +2,6 @@ use std::cell::RefCell; use std::fmt; use std::future::Future; use std::rc::Rc; -use std::task::Poll; use actix_http::Extensions; use actix_router::{ResourceDef, Router}; From c8f6d37290e907a79a3acf1a20ad6327ba5bfd65 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Tue, 16 Mar 2021 09:31:14 -0700 Subject: [PATCH 21/36] rename client io trait. reduce duplicate code (#2079) --- actix-http/src/client/connection.rs | 4 ++ actix-http/src/client/connector.rs | 22 ++++----- actix-http/src/client/mod.rs | 2 +- awc/src/connect.rs | 73 ++++------------------------- 4 files changed, 26 insertions(+), 75 deletions(-) diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index 9fb5c58cc..291148b5b 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -19,6 +19,10 @@ use super::error::SendRequestError; use super::pool::Acquired; use super::{h1proto, h2proto}; +pub trait ConnectionIo: AsyncRead + AsyncWrite + Unpin + 'static {} + +impl ConnectionIo for T {} + pub(crate) enum ConnectionType { H1(Io), H2(H2Connection), diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index ebdbbb6eb..0c9159b87 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -18,7 +18,7 @@ use futures_core::ready; use http::Uri; use super::config::ConnectorConfig; -use super::connection::{Connection, EitherIoConnection}; +use super::connection::{Connection, ConnectionIo, EitherIoConnection}; use super::error::ConnectError; use super::pool::ConnectionPool; use super::Connect; @@ -61,9 +61,6 @@ pub struct Connector { ssl: SslConnector, } -pub trait Io: AsyncRead + AsyncWrite + Unpin {} -impl Io for T {} - impl Connector<()> { #[allow(clippy::new_ret_no_self, clippy::let_unit_value)] pub fn new() -> Connector< @@ -281,16 +278,16 @@ where pub type DummyService = Box< dyn Service< Connect, - Response = (Box, Protocol), + Response = (Box, Protocol), Error = ConnectError, Future = futures_core::future::LocalBoxFuture< 'static, - Result<(Box, Protocol), ConnectError>, + Result<(Box, Protocol), ConnectError>, >, >, >; - InnerConnector::<_, DummyService, _, Box> { + InnerConnector::<_, DummyService, _, Box> { tcp_pool: ConnectionPool::new( tcp_service, self.config.no_disconnect_timeout(), @@ -334,9 +331,12 @@ where .map(|protos| protos.windows(2).any(|w| w == H2)) .unwrap_or(false); if h2 { - (Box::new(sock) as Box, Protocol::Http2) + ( + Box::new(sock) as Box, + Protocol::Http2, + ) } else { - (Box::new(sock) as Box, Protocol::Http1) + (Box::new(sock) as _, Protocol::Http1) } }) .map_err(ConnectError::from), @@ -354,9 +354,9 @@ where .map(|protos| protos.windows(2).any(|w| w == H2)) .unwrap_or(false); if h2 { - (Box::new(sock) as Box, Protocol::Http2) + (Box::new(sock) as _, Protocol::Http2) } else { - (Box::new(sock) as Box, Protocol::Http1) + (Box::new(sock) as _, Protocol::Http1) } }), ), diff --git a/actix-http/src/client/mod.rs b/actix-http/src/client/mod.rs index c6f998c2a..ce6aa3bc1 100644 --- a/actix-http/src/client/mod.rs +++ b/actix-http/src/client/mod.rs @@ -14,7 +14,7 @@ pub use actix_tls::connect::{ Connect as TcpConnect, ConnectError as TcpConnectError, Connection as TcpConnection, }; -pub use self::connection::Connection; +pub use self::connection::{Connection, ConnectionIo}; pub use self::connector::Connector; pub use self::error::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError}; pub use crate::Protocol; diff --git a/awc/src/connect.rs b/awc/src/connect.rs index a4abbc46b..d3bc01ed6 100644 --- a/awc/src/connect.rs +++ b/awc/src/connect.rs @@ -1,15 +1,16 @@ use std::{ - fmt, future::Future, - io, net, + net, pin::Pin, task::{Context, Poll}, }; -use actix_codec::{AsyncRead, AsyncWrite, Framed, ReadBuf}; +use actix_codec::Framed; use actix_http::{ body::Body, - client::{Connect as ClientConnect, ConnectError, Connection, SendRequestError}, + client::{ + Connect as ClientConnect, ConnectError, Connection, ConnectionIo, SendRequestError, + }, h1::ClientCodec, Payload, RequestHead, RequestHeadType, ResponseHead, }; @@ -123,7 +124,7 @@ impl Future for ConnectRequestFuture where Fut: Future>, C: Connection, - Io: AsyncRead + AsyncWrite + Unpin + 'static, + Io: ConnectionIo, { type Output = Result; @@ -138,14 +139,14 @@ where let fut = ConnectRequestFuture::Client { fut: connection.send_request(head, body), }; - self.as_mut().set(fut); + self.set(fut); } ConnectRequest::Tunnel(head, ..) => { // send request let fut = ConnectRequestFuture::Tunnel { fut: connection.open_tunnel(RequestHeadType::from(head)), }; - self.as_mut().set(fut); + self.set(fut); } } self.poll(cx) @@ -158,65 +159,11 @@ where } ConnectRequestProj::Tunnel { fut } => { let (head, framed) = ready!(fut.as_mut().poll(cx))?; - let framed = framed.into_map_io(|io| BoxedSocket(Box::new(Socket(io)))); + let framed = framed.into_map_io(|io| Box::new(io) as _); Poll::Ready(Ok(ConnectResponse::Tunnel(head, framed))) } } } } -trait AsyncSocket { - fn as_read(&self) -> &(dyn AsyncRead + Unpin); - fn as_read_mut(&mut self) -> &mut (dyn AsyncRead + Unpin); - fn as_write(&mut self) -> &mut (dyn AsyncWrite + Unpin); -} - -struct Socket(T); - -impl AsyncSocket for Socket { - fn as_read(&self) -> &(dyn AsyncRead + Unpin) { - &self.0 - } - fn as_read_mut(&mut self) -> &mut (dyn AsyncRead + Unpin) { - &mut self.0 - } - fn as_write(&mut self) -> &mut (dyn AsyncWrite + Unpin) { - &mut self.0 - } -} - -pub struct BoxedSocket(Box); - -impl fmt::Debug for BoxedSocket { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "BoxedSocket") - } -} - -impl AsyncRead for BoxedSocket { - fn poll_read( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - Pin::new(self.get_mut().0.as_read_mut()).poll_read(cx, buf) - } -} - -impl AsyncWrite for BoxedSocket { - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - Pin::new(self.get_mut().0.as_write()).poll_write(cx, buf) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(self.get_mut().0.as_write()).poll_flush(cx) - } - - fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(self.get_mut().0.as_write()).poll_shutdown(cx) - } -} +pub type BoxedSocket = Box; From 3dc2d145efcde2feb3e5f20d9002390c283b1ea4 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 11 Mar 2021 03:48:38 +0000 Subject: [PATCH 22/36] import some traits as _ --- actix-http/examples/echo.rs | 2 +- actix-http/examples/echo2.rs | 2 +- actix-http/src/client/h1proto.rs | 4 ++-- actix-http/src/h2/dispatcher.rs | 6 ++++-- actix-http/src/h2/service.rs | 4 ++-- actix-http/src/service.rs | 19 ++++++++++++------- actix-http/src/time_parser.rs | 2 +- actix-http/tests/test_client.rs | 2 +- actix-http/tests/test_openssl.rs | 7 +++++-- actix-http/tests/test_rustls.rs | 3 ++- actix-http/tests/test_server.rs | 2 +- actix-http/tests/test_ws.rs | 2 +- 12 files changed, 33 insertions(+), 22 deletions(-) diff --git a/actix-http/examples/echo.rs b/actix-http/examples/echo.rs index 90d768cbe..176ac5c2b 100644 --- a/actix-http/examples/echo.rs +++ b/actix-http/examples/echo.rs @@ -3,7 +3,7 @@ use std::{env, io}; use actix_http::{Error, HttpService, Request, Response}; use actix_server::Server; use bytes::BytesMut; -use futures_util::StreamExt; +use futures_util::StreamExt as _; use http::header::HeaderValue; use log::info; diff --git a/actix-http/examples/echo2.rs b/actix-http/examples/echo2.rs index bc932ce8f..408a40114 100644 --- a/actix-http/examples/echo2.rs +++ b/actix-http/examples/echo2.rs @@ -4,7 +4,7 @@ use actix_http::http::HeaderValue; use actix_http::{Error, HttpService, Request, Response}; use actix_server::Server; use bytes::BytesMut; -use futures_util::StreamExt; +use futures_util::StreamExt as _; use log::info; async fn handle_request(mut req: Request) -> Result { diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index cd0b45734..75a3c1f3d 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -7,7 +7,7 @@ use actix_codec::{AsyncRead, AsyncWrite, Framed, ReadBuf}; use bytes::buf::BufMut; use bytes::{Bytes, BytesMut}; use futures_core::Stream; -use futures_util::{future::poll_fn, SinkExt}; +use futures_util::{future::poll_fn, SinkExt as _}; use crate::error::PayloadError; use crate::h1; @@ -198,7 +198,7 @@ where } } - SinkExt::flush(framed.get_mut()).await?; + framed.get_mut().flush().await?; Ok(()) } diff --git a/actix-http/src/h2/dispatcher.rs b/actix-http/src/h2/dispatcher.rs index 958c761d5..6e6cd5a2f 100644 --- a/actix-http/src/h2/dispatcher.rs +++ b/actix-http/src/h2/dispatcher.rs @@ -5,8 +5,10 @@ use actix_codec::{AsyncRead, AsyncWrite}; use actix_service::Service; use bytes::{Bytes, BytesMut}; use futures_core::ready; -use h2::server::{Connection, SendResponse}; -use h2::SendStream; +use h2::{ + server::{Connection, SendResponse}, + SendStream, +}; use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING}; use log::{error, trace}; diff --git a/actix-http/src/h2/service.rs b/actix-http/src/h2/service.rs index 1dc290e49..c64139564 100644 --- a/actix-http/src/h2/service.rs +++ b/actix-http/src/h2/service.rs @@ -13,7 +13,7 @@ use actix_service::{ use bytes::Bytes; use futures_core::ready; use futures_util::future::ok; -use h2::server::{self, Handshake}; +use h2::server::{handshake, Handshake}; use log::error; use crate::body::MessageBody; @@ -307,7 +307,7 @@ where Some(self.cfg.clone()), addr, on_connect_data, - server::handshake(io), + handshake(io), ), } } diff --git a/actix-http/src/service.rs b/actix-http/src/service.rs index 89f3e3bb1..1a06cec3d 100644 --- a/actix-http/src/service.rs +++ b/actix-http/src/service.rs @@ -1,14 +1,19 @@ -use std::marker::PhantomData; -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::{fmt, net, rc::Rc}; +use std::{ + fmt, + future::Future, + marker::PhantomData, + net, + pin::Pin, + rc::Rc, + task::{Context, Poll}, +}; use actix_codec::{AsyncRead, AsyncWrite, Framed}; use actix_rt::net::TcpStream; use actix_service::{pipeline_factory, IntoServiceFactory, Service, ServiceFactory}; use bytes::Bytes; -use futures_core::{ready, Future}; -use h2::server::{self, Handshake}; +use futures_core::ready; +use h2::server::{handshake, Handshake}; use pin_project::pin_project; use crate::body::MessageBody; @@ -562,7 +567,7 @@ where match proto { Protocol::Http2 => HttpServiceHandlerResponse { state: State::H2Handshake(Some(( - server::handshake(io), + handshake(io), self.cfg.clone(), self.flow.clone(), on_connect_data, diff --git a/actix-http/src/time_parser.rs b/actix-http/src/time_parser.rs index 46bf73037..fd82fd42e 100644 --- a/actix-http/src/time_parser.rs +++ b/actix-http/src/time_parser.rs @@ -1,7 +1,7 @@ use time::{Date, OffsetDateTime, PrimitiveDateTime}; /// Attempt to parse a `time` string as one of either RFC 1123, RFC 850, or asctime. -pub fn parse_http_date(time: &str) -> Option { +pub(crate) fn parse_http_date(time: &str) -> Option { try_parse_rfc_1123(time) .or_else(|| try_parse_rfc_850(time)) .or_else(|| try_parse_asctime(time)) diff --git a/actix-http/tests/test_client.rs b/actix-http/tests/test_client.rs index a50f2404d..758e39745 100644 --- a/actix-http/tests/test_client.rs +++ b/actix-http/tests/test_client.rs @@ -6,7 +6,7 @@ use actix_service::ServiceFactoryExt; use bytes::Bytes; use futures_util::{ future::{self, ok}, - StreamExt, + StreamExt as _, }; const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ diff --git a/actix-http/tests/test_openssl.rs b/actix-http/tests/test_openssl.rs index d5ec645a4..49a68a60d 100644 --- a/actix-http/tests/test_openssl.rs +++ b/actix-http/tests/test_openssl.rs @@ -12,8 +12,11 @@ use actix_http::{body, Error, HttpService, Request, Response}; use actix_http_test::test_server; use actix_service::{fn_service, ServiceFactoryExt}; use bytes::{Bytes, BytesMut}; -use futures_util::future::{err, ok, ready}; -use futures_util::stream::{once, Stream, StreamExt}; +use futures_core::Stream; +use futures_util::{ + future::{err, ok, ready}, + stream::{once, StreamExt as _}, +}; use openssl::{ pkey::PKey, ssl::{SslAcceptor, SslMethod}, diff --git a/actix-http/tests/test_rustls.rs b/actix-http/tests/test_rustls.rs index 81edb5c18..7a3cb1473 100644 --- a/actix-http/tests/test_rustls.rs +++ b/actix-http/tests/test_rustls.rs @@ -10,8 +10,9 @@ use actix_http_test::test_server; use actix_service::{fn_factory_with_config, fn_service}; use bytes::{Bytes, BytesMut}; +use futures_core::Stream; use futures_util::future::{self, err, ok}; -use futures_util::stream::{once, Stream, StreamExt}; +use futures_util::stream::{once, StreamExt as _}; use rustls::{ internal::pemfile::{certs, pkcs8_private_keys}, NoClientAuth, ServerConfig as RustlsServerConfig, diff --git a/actix-http/tests/test_server.rs b/actix-http/tests/test_server.rs index a4c1f92b5..6d145400c 100644 --- a/actix-http/tests/test_server.rs +++ b/actix-http/tests/test_server.rs @@ -7,7 +7,7 @@ use actix_rt::time::sleep; use actix_service::fn_service; use bytes::Bytes; use futures_util::future::{self, err, ok, ready, FutureExt}; -use futures_util::stream::{once, StreamExt}; +use futures_util::stream::{once, StreamExt as _}; use regex::Regex; use actix_http::HttpMessage; diff --git a/actix-http/tests/test_ws.rs b/actix-http/tests/test_ws.rs index 7ed9b0df1..3b90b4e54 100644 --- a/actix-http/tests/test_ws.rs +++ b/actix-http/tests/test_ws.rs @@ -12,7 +12,7 @@ use actix_utils::dispatcher::Dispatcher; use bytes::Bytes; use futures_util::future; use futures_util::task::{Context, Poll}; -use futures_util::{SinkExt, StreamExt}; +use futures_util::{SinkExt as _, StreamExt as _}; struct WsService(Arc, Cell)>>); From 983b6904a760134c00cff83edf2d83ffab96a590 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Wed, 17 Mar 2021 00:38:42 +0000 Subject: [PATCH 23/36] unvendor openssl --- .github/workflows/ci.yml | 15 +++++++++++++++ Cargo.toml | 6 ------ actix-http-test/Cargo.toml | 6 ------ actix-http/Cargo.toml | 5 ----- awc/Cargo.toml | 6 ------ 5 files changed, 15 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d0520d52..dc0ff0c19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,21 @@ jobs: steps: - uses: actions/checkout@v2 + # install OpenSSL on Windows + - name: Set vcpkg root + if: matrix.target.triple == 'x86_64-pc-windows-msvc' + run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append + - name: Install OpenSSL + if: matrix.target.triple == 'x86_64-pc-windows-msvc' + run: vcpkg install openssl:x64-windows + + - name: Install ${{ matrix.version }} + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.version }}-${{ matrix.target.triple }} + profile: minimal + override: true + - name: Install ${{ matrix.version }} uses: actions-rs/toolchain@v1 with: diff --git a/Cargo.toml b/Cargo.toml index 8477c8ede..0877edf9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,12 +111,6 @@ tls-openssl = { package = "openssl", version = "0.10.9", optional = true } tls-rustls = { package = "rustls", version = "0.19.0", optional = true } url = "2.1" -[target.'cfg(windows)'.dependencies.tls-openssl] -version = "0.10.9" -package = "openssl" -features = ["vendored"] -optional = true - [dev-dependencies] brotli2 = "0.3.2" criterion = "0.3" diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index 69b2a3335..c1e61556b 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -50,12 +50,6 @@ serde_urlencoded = "0.7" time = { version = "0.2.23", default-features = false, features = ["std"] } tls-openssl = { version = "0.10.9", package = "openssl", optional = true } -[target.'cfg(windows)'.dependencies.tls-openssl] -version = "0.10.9" -package = "openssl" -features = ["vendored"] -optional = true - [dev-dependencies] actix-web = { version = "4.0.0-beta.4", default-features = false, features = ["cookies"] } actix-http = "3.0.0-beta.4" diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index edcc7efd5..e1aebb76b 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -98,11 +98,6 @@ serde_derive = "1.0" tls-openssl = { version = "0.10", package = "openssl" } tls-rustls = { version = "0.19", package = "rustls" } -[target.'cfg(windows)'.dev-dependencies.tls-openssl] -version = "0.10.9" -package = "openssl" -features = ["vendored"] - [[example]] name = "ws" required-features = ["rustls"] diff --git a/awc/Cargo.toml b/awc/Cargo.toml index 981b93a52..ec2e03a96 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -66,12 +66,6 @@ serde_urlencoded = "0.7" tls-openssl = { version = "0.10.9", package = "openssl", optional = true } tls-rustls = { version = "0.19.0", package = "rustls", optional = true, features = ["dangerous_configuration"] } -[target.'cfg(windows)'.dependencies.tls-openssl] -version = "0.10.9" -package = "openssl" -features = ["vendored"] -optional = true - [dev-dependencies] actix-web = { version = "4.0.0-beta.4", features = ["openssl"] } actix-http = { version = "3.0.0-beta.4", features = ["openssl"] } From abcb444dd90b82e96512bfa4ba41fe041860561e Mon Sep 17 00:00:00 2001 From: obayemi Date: Thu, 18 Mar 2021 14:21:44 +0100 Subject: [PATCH 24/36] fix routes in Path documentation (#2084) --- src/types/path.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/path.rs b/src/types/path.rs index 4ab124d53..294df6cf2 100644 --- a/src/types/path.rs +++ b/src/types/path.rs @@ -20,7 +20,7 @@ use crate::{dev::Payload, error::PathError, FromRequest, HttpRequest}; /// // extract path info from "/{name}/{count}/index.html" into tuple /// // {name} - deserialize a String /// // {count} - deserialize a u32 -/// #[get("/")] +/// #[get("/{name}/{count}/index.html")] /// async fn index(path: web::Path<(String, u32)>) -> String { /// let (name, count) = path.into_inner(); /// format!("Welcome {}! {}", name, count) @@ -40,7 +40,7 @@ use crate::{dev::Payload, error::PathError, FromRequest, HttpRequest}; /// } /// /// // extract `Info` from a path using serde -/// #[get("/")] +/// #[get("/{name}")] /// async fn index(info: web::Path) -> String { /// format!("Welcome {}!", info.name) /// } From b75b5114c30bf56692290775e012a152d13f0122 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Thu, 18 Mar 2021 10:53:22 -0700 Subject: [PATCH 25/36] refactor actix_http connection types and connector services (#2081) --- actix-http/CHANGES.md | 6 + actix-http/src/client/config.rs | 2 + actix-http/src/client/connection.rs | 491 +++++++++++++++--------- actix-http/src/client/connector.rs | 575 +++++++++++++++++++--------- actix-http/src/client/h1proto.rs | 136 ++----- actix-http/src/client/h2proto.rs | 38 +- actix-http/src/client/mod.rs | 2 +- actix-http/src/client/pool.rs | 120 +++--- awc/CHANGES.md | 4 + awc/src/builder.rs | 20 +- awc/src/connect.rs | 30 +- awc/src/frozen.rs | 22 +- awc/src/lib.rs | 9 +- awc/src/middleware/redirect.rs | 4 +- awc/src/request.rs | 14 +- awc/src/ws.rs | 5 +- 16 files changed, 867 insertions(+), 611 deletions(-) diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index e96c22274..c4e0aec89 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,10 +1,16 @@ # Changes ## Unreleased - 2021-xx-xx +### Added +* `client::Connector::handshake_timeout` method for customize tls connection handshake timeout. [#2081] +* `client::ConnectorService` as `client::Connector::finish` method's return type [#2081] +* `client::ConnectionIo` trait alias [#2081] + ### Chaged * `client::Connector` type now only have one generic type for `actix_service::Service`. [#2063] [#2063]: https://github.com/actix/actix-web/pull/2063 +[#2081]: https://github.com/actix/actix-web/pull/2081 ## 3.0.0-beta.4 - 2021-03-08 diff --git a/actix-http/src/client/config.rs b/actix-http/src/client/config.rs index 0d54e1b49..1c0405cbc 100644 --- a/actix-http/src/client/config.rs +++ b/actix-http/src/client/config.rs @@ -8,6 +8,7 @@ const DEFAULT_H2_STREAM_WINDOW: u32 = 1024 * 1024; // 1MB #[derive(Clone)] pub(crate) struct ConnectorConfig { pub(crate) timeout: Duration, + pub(crate) handshake_timeout: Duration, pub(crate) conn_lifetime: Duration, pub(crate) conn_keep_alive: Duration, pub(crate) disconnect_timeout: Option, @@ -21,6 +22,7 @@ impl Default for ConnectorConfig { fn default() -> Self { Self { timeout: Duration::from_secs(5), + handshake_timeout: Duration::from_secs(5), conn_lifetime: Duration::from_secs(75), conn_keep_alive: Duration::from_secs(15), disconnect_timeout: Some(Duration::from_millis(3000)), diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index 291148b5b..89dfd59de 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -1,14 +1,16 @@ -use std::ops::{Deref, DerefMut}; -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::{fmt, io, time}; +use std::{ + io, + ops::{Deref, DerefMut}, + pin::Pin, + task::{Context, Poll}, + time, +}; use actix_codec::{AsyncRead, AsyncWrite, Framed, ReadBuf}; use actix_rt::task::JoinHandle; use bytes::Bytes; use futures_core::future::LocalBoxFuture; use h2::client::SendRequest; -use pin_project::pin_project; use crate::body::MessageBody; use crate::h1::ClientCodec; @@ -19,32 +21,148 @@ use super::error::SendRequestError; use super::pool::Acquired; use super::{h1proto, h2proto}; +/// Trait alias for types impl [tokio::io::AsyncRead] and [tokio::io::AsyncWrite]. pub trait ConnectionIo: AsyncRead + AsyncWrite + Unpin + 'static {} impl ConnectionIo for T {} -pub(crate) enum ConnectionType { - H1(Io), - H2(H2Connection), +/// HTTP client connection +pub struct H1Connection { + io: Option, + created: time::Instant, + acquired: Acquired, } -/// `H2Connection` has two parts: `SendRequest` and `Connection`. +impl H1Connection { + /// close or release the connection to pool based on flag input + pub(super) fn on_release(&mut self, keep_alive: bool) { + if keep_alive { + self.release(); + } else { + self.close(); + } + } + + /// Close connection + fn close(&mut self) { + let io = self.io.take().unwrap(); + self.acquired.close(ConnectionInnerType::H1(io)); + } + + /// Release this connection to the connection pool + fn release(&mut self) { + let io = self.io.take().unwrap(); + self.acquired + .release(ConnectionInnerType::H1(io), self.created); + } + + fn io_pin_mut(self: Pin<&mut Self>) -> Pin<&mut Io> { + Pin::new(self.get_mut().io.as_mut().unwrap()) + } +} + +impl AsyncRead for H1Connection { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + self.io_pin_mut().poll_read(cx, buf) + } +} + +impl AsyncWrite for H1Connection { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + self.io_pin_mut().poll_write(cx, buf) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.io_pin_mut().poll_flush(cx) + } + + fn poll_shutdown( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + self.io_pin_mut().poll_shutdown(cx) + } + + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[io::IoSlice<'_>], + ) -> Poll> { + self.io_pin_mut().poll_write_vectored(cx, bufs) + } + + fn is_write_vectored(&self) -> bool { + self.io.as_ref().unwrap().is_write_vectored() + } +} + +/// HTTP2 client connection +pub struct H2Connection { + io: Option, + created: time::Instant, + acquired: Acquired, +} + +impl Deref for H2Connection { + type Target = SendRequest; + + fn deref(&self) -> &Self::Target { + &self.io.as_ref().unwrap().sender + } +} + +impl DerefMut for H2Connection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.io.as_mut().unwrap().sender + } +} + +impl H2Connection { + /// close or release the connection to pool based on flag input + pub(super) fn on_release(&mut self, close: bool) { + if close { + self.close(); + } else { + self.release(); + } + } + + /// Close connection + fn close(&mut self) { + let io = self.io.take().unwrap(); + self.acquired.close(ConnectionInnerType::H2(io)); + } + + /// Release this connection to the connection pool + fn release(&mut self) { + let io = self.io.take().unwrap(); + self.acquired + .release(ConnectionInnerType::H2(io), self.created); + } +} + +/// `H2ConnectionInner` has two parts: `SendRequest` and `Connection`. /// -/// `Connection` is spawned as an async task on runtime and `H2Connection` holds a handle for -/// this task. Therefore, it can wake up and quit the task when SendRequest is dropped. -pub(crate) struct H2Connection { +/// `Connection` is spawned as an async task on runtime and `H2ConnectionInner` holds a handle +/// for this task. Therefore, it can wake up and quit the task when SendRequest is dropped. +pub(super) struct H2ConnectionInner { handle: JoinHandle<()>, sender: SendRequest, } -impl H2Connection { - pub(crate) fn new( +impl H2ConnectionInner { + pub(super) fn new( sender: SendRequest, connection: h2::client::Connection, - ) -> Self - where - Io: AsyncRead + AsyncWrite + Unpin + 'static, - { + ) -> Self { let handle = actix_rt::spawn(async move { let _ = connection.await; }); @@ -53,143 +171,80 @@ impl H2Connection { } } -// cancel spawned connection task on drop. -impl Drop for H2Connection { +/// Cancel spawned connection task on drop. +impl Drop for H2ConnectionInner { fn drop(&mut self) { self.handle.abort(); } } -// only expose sender type to public. -impl Deref for H2Connection { - type Target = SendRequest; - - fn deref(&self) -> &Self::Target { - &self.sender - } -} - -impl DerefMut for H2Connection { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.sender - } -} - -pub trait Connection { - type Io: AsyncRead + AsyncWrite + Unpin; - - /// Send request and body - fn send_request( - self, - head: H, - body: B, - ) -> LocalBoxFuture<'static, Result<(ResponseHead, Payload), SendRequestError>> - where - B: MessageBody + 'static, - H: Into + 'static; - - /// Send request, returns Response and Framed - fn open_tunnel + 'static>( - self, - head: H, - ) -> LocalBoxFuture< - 'static, - Result<(ResponseHead, Framed), SendRequestError>, - >; -} - -#[doc(hidden)] -/// HTTP client connection -pub struct IoConnection -where - T: AsyncWrite + Unpin + 'static, -{ - io: Option>, - created: time::Instant, - pool: Acquired, -} - -impl fmt::Debug for IoConnection -where - T: AsyncWrite + Unpin + fmt::Debug + 'static, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.io { - Some(ConnectionType::H1(ref io)) => write!(f, "H1Connection({:?})", io), - Some(ConnectionType::H2(_)) => write!(f, "H2Connection"), - None => write!(f, "Connection(Empty)"), - } - } -} - -impl IoConnection { - pub(crate) fn new( - io: ConnectionType, - created: time::Instant, - pool: Acquired, - ) -> Self { - IoConnection { - pool, - created, - io: Some(io), - } - } - - #[cfg(test)] - pub(crate) fn into_parts(self) -> (ConnectionType, time::Instant, Acquired) { - (self.io.unwrap(), self.created, self.pool) - } - - async fn send_request>( - mut self, - head: H, - body: B, - ) -> Result<(ResponseHead, Payload), SendRequestError> { - match self.io.take().unwrap() { - ConnectionType::H1(io) => { - h1proto::send_request(io, head.into(), body, self.created, self.pool) - .await - } - ConnectionType::H2(io) => { - h2proto::send_request(io, head.into(), body, self.created, self.pool) - .await - } - } - } - - /// Send request, returns Response and Framed - async fn open_tunnel>( - mut self, - head: H, - ) -> Result<(ResponseHead, Framed), SendRequestError> { - match self.io.take().unwrap() { - ConnectionType::H1(io) => h1proto::open_tunnel(io, head.into()).await, - ConnectionType::H2(io) => { - self.pool.release(ConnectionType::H2(io), self.created); - Err(SendRequestError::TunnelNotSupported) - } - } - } -} - #[allow(dead_code)] -pub(crate) enum EitherIoConnection +/// Unified connection type cover Http1 Plain/Tls and Http2 protocols +pub enum Connection> where - A: AsyncRead + AsyncWrite + Unpin + 'static, - B: AsyncRead + AsyncWrite + Unpin + 'static, + A: ConnectionIo, + B: ConnectionIo, { - A(IoConnection), - B(IoConnection), + Tcp(ConnectionType), + Tls(ConnectionType), } -impl Connection for EitherIoConnection -where - A: AsyncRead + AsyncWrite + Unpin + 'static, - B: AsyncRead + AsyncWrite + Unpin + 'static, -{ - type Io = EitherIo; +/// Unified connection type cover Http1/2 protocols +pub enum ConnectionType { + H1(H1Connection), + H2(H2Connection), +} - fn send_request( +/// Helper type for storing connection types in pool. +pub(super) enum ConnectionInnerType { + H1(Io), + H2(H2ConnectionInner), +} + +impl ConnectionType { + pub(super) fn from_pool( + inner: ConnectionInnerType, + created: time::Instant, + acquired: Acquired, + ) -> Self { + match inner { + ConnectionInnerType::H1(io) => Self::from_h1(io, created, acquired), + ConnectionInnerType::H2(io) => Self::from_h2(io, created, acquired), + } + } + + pub(super) fn from_h1( + io: Io, + created: time::Instant, + acquired: Acquired, + ) -> Self { + Self::H1(H1Connection { + io: Some(io), + created, + acquired, + }) + } + + pub(super) fn from_h2( + io: H2ConnectionInner, + created: time::Instant, + acquired: Acquired, + ) -> Self { + Self::H2(H2Connection { + io: Some(io), + created, + acquired, + }) + } +} + +impl Connection +where + A: ConnectionIo, + B: ConnectionIo, +{ + /// Send a request through connection. + pub fn send_request( self, head: H, body: RB, @@ -198,76 +253,106 @@ where RB: MessageBody + 'static, H: Into + 'static, { - match self { - EitherIoConnection::A(con) => Box::pin(con.send_request(head, body)), - EitherIoConnection::B(con) => Box::pin(con.send_request(head, body)), - } + Box::pin(async move { + match self { + Connection::Tcp(ConnectionType::H1(conn)) => { + h1proto::send_request(conn, head.into(), body).await + } + Connection::Tls(ConnectionType::H1(conn)) => { + h1proto::send_request(conn, head.into(), body).await + } + Connection::Tls(ConnectionType::H2(conn)) => { + h2proto::send_request(conn, head.into(), body).await + } + _ => unreachable!( + "Plain Tcp connection can be used only in Http1 protocol" + ), + } + }) } - /// Send request, returns Response and Framed - fn open_tunnel + 'static>( + /// Send request, returns Response and Framed tunnel. + pub fn open_tunnel + 'static>( self, head: H, ) -> LocalBoxFuture< 'static, - Result<(ResponseHead, Framed), SendRequestError>, + Result<(ResponseHead, Framed, ClientCodec>), SendRequestError>, > { - match self { - EitherIoConnection::A(con) => Box::pin(async { - let (head, framed) = con.open_tunnel(head).await?; - Ok((head, framed.into_map_io(EitherIo::A))) - }), - EitherIoConnection::B(con) => Box::pin(async { - let (head, framed) = con.open_tunnel(head).await?; - Ok((head, framed.into_map_io(EitherIo::B))) - }), - } + Box::pin(async move { + match self { + Connection::Tcp(ConnectionType::H1(ref _conn)) => { + let (head, framed) = h1proto::open_tunnel(self, head.into()).await?; + Ok((head, framed)) + } + Connection::Tls(ConnectionType::H1(ref _conn)) => { + let (head, framed) = h1proto::open_tunnel(self, head.into()).await?; + Ok((head, framed)) + } + Connection::Tls(ConnectionType::H2(mut conn)) => { + conn.release(); + Err(SendRequestError::TunnelNotSupported) + } + Connection::Tcp(ConnectionType::H2(_)) => { + unreachable!( + "Plain Tcp connection can be used only in Http1 protocol" + ) + } + } + }) } } -#[pin_project(project = EitherIoProj)] -pub enum EitherIo { - A(#[pin] A), - B(#[pin] B), -} - -impl AsyncRead for EitherIo +impl AsyncRead for Connection where - A: AsyncRead, - B: AsyncRead, + A: ConnectionIo, + B: ConnectionIo, { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { - match self.project() { - EitherIoProj::A(val) => val.poll_read(cx, buf), - EitherIoProj::B(val) => val.poll_read(cx, buf), + match self.get_mut() { + Connection::Tcp(ConnectionType::H1(conn)) => { + Pin::new(conn).poll_read(cx, buf) + } + Connection::Tls(ConnectionType::H1(conn)) => { + Pin::new(conn).poll_read(cx, buf) + } + _ => unreachable!("H2Connection can not impl AsyncRead trait"), } } } -impl AsyncWrite for EitherIo +const H2_UNREACHABLE_WRITE: &'static str = "H2Connection can not impl AsyncWrite trait"; + +impl AsyncWrite for Connection where - A: AsyncWrite, - B: AsyncWrite, + A: ConnectionIo, + B: ConnectionIo, { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - match self.project() { - EitherIoProj::A(val) => val.poll_write(cx, buf), - EitherIoProj::B(val) => val.poll_write(cx, buf), + match self.get_mut() { + Connection::Tcp(ConnectionType::H1(conn)) => { + Pin::new(conn).poll_write(cx, buf) + } + Connection::Tls(ConnectionType::H1(conn)) => { + Pin::new(conn).poll_write(cx, buf) + } + _ => unreachable!(H2_UNREACHABLE_WRITE), } } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - match self.project() { - EitherIoProj::A(val) => val.poll_flush(cx), - EitherIoProj::B(val) => val.poll_flush(cx), + match self.get_mut() { + Connection::Tcp(ConnectionType::H1(conn)) => Pin::new(conn).poll_flush(cx), + Connection::Tls(ConnectionType::H1(conn)) => Pin::new(conn).poll_flush(cx), + _ => unreachable!(H2_UNREACHABLE_WRITE), } } @@ -275,9 +360,38 @@ where self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { - match self.project() { - EitherIoProj::A(val) => val.poll_shutdown(cx), - EitherIoProj::B(val) => val.poll_shutdown(cx), + match self.get_mut() { + Connection::Tcp(ConnectionType::H1(conn)) => { + Pin::new(conn).poll_shutdown(cx) + } + Connection::Tls(ConnectionType::H1(conn)) => { + Pin::new(conn).poll_shutdown(cx) + } + _ => unreachable!(H2_UNREACHABLE_WRITE), + } + } + + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[io::IoSlice<'_>], + ) -> Poll> { + match self.get_mut() { + Connection::Tcp(ConnectionType::H1(conn)) => { + Pin::new(conn).poll_write_vectored(cx, bufs) + } + Connection::Tls(ConnectionType::H1(conn)) => { + Pin::new(conn).poll_write_vectored(cx, bufs) + } + _ => unreachable!(H2_UNREACHABLE_WRITE), + } + } + + fn is_write_vectored(&self) -> bool { + match *self { + Connection::Tcp(ConnectionType::H1(ref conn)) => conn.is_write_vectored(), + Connection::Tls(ConnectionType::H1(ref conn)) => conn.is_write_vectored(), + _ => unreachable!(H2_UNREACHABLE_WRITE), } } } @@ -300,10 +414,13 @@ mod test { let tcp = TcpStream::connect(local).await.unwrap(); let (sender, connection) = h2::client::handshake(tcp).await.unwrap(); - let conn = H2Connection::new(sender.clone(), connection); + let conn = H2ConnectionInner::new(sender.clone(), connection); assert!(sender.clone().ready().await.is_ok()); - assert!(h2::client::SendRequest::clone(&*conn).ready().await.is_ok()); + assert!(h2::client::SendRequest::clone(&conn.sender) + .ready() + .await + .is_ok()); drop(conn); diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 0c9159b87..1a5f32880 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -3,22 +3,26 @@ use std::{ future::Future, net::IpAddr, pin::Pin, + rc::Rc, task::{Context, Poll}, time::Duration, }; -use actix_codec::{AsyncRead, AsyncWrite}; -use actix_rt::net::TcpStream; -use actix_service::{apply_fn, Service, ServiceExt}; -use actix_tls::connect::{ - new_connector, Connect as TcpConnect, Connection as TcpConnection, Resolver, +use actix_rt::{ + net::TcpStream, + time::{sleep, Sleep}, }; -use actix_utils::timeout::{TimeoutError, TimeoutService}; -use futures_core::ready; +use actix_service::Service; +use actix_tls::connect::{ + new_connector, Connect as TcpConnect, ConnectError as TcpConnectError, + Connection as TcpConnection, Resolver, +}; +use futures_core::{future::LocalBoxFuture, ready}; use http::Uri; +use pin_project::pin_project; use super::config::ConnectorConfig; -use super::connection::{Connection, ConnectionIo, EitherIoConnection}; +use super::connection::{Connection, ConnectionIo}; use super::error::ConnectError; use super::pool::ConnectionPool; use super::Connect; @@ -28,18 +32,15 @@ use super::Protocol; use actix_tls::connect::ssl::openssl::SslConnector as OpensslConnector; #[cfg(feature = "rustls")] use actix_tls::connect::ssl::rustls::ClientConfig; -#[cfg(feature = "rustls")] -use std::sync::Arc; -#[cfg(any(feature = "openssl", feature = "rustls"))] enum SslConnector { + #[allow(dead_code)] + None, #[cfg(feature = "openssl")] Openssl(OpensslConnector), #[cfg(feature = "rustls")] - Rustls(Arc), + Rustls(std::sync::Arc), } -#[cfg(not(any(feature = "openssl", feature = "rustls")))] -type SslConnector = (); /// Manages HTTP client network connectivity. /// @@ -104,23 +105,25 @@ impl Connector<()> { config.root_store.add_server_trust_anchors( &actix_tls::connect::ssl::rustls::TLS_SERVER_ROOTS, ); - SslConnector::Rustls(Arc::new(config)) + SslConnector::Rustls(std::sync::Arc::new(config)) } // ssl turned off, provides empty ssl connector #[cfg(not(any(feature = "openssl", feature = "rustls")))] - fn build_ssl(_: Vec>) -> SslConnector {} + fn build_ssl(_: Vec>) -> SslConnector { + SslConnector::None + } } -impl Connector { +impl Connector { /// Use custom connector. - pub fn connector(self, connector: T1) -> Connector + pub fn connector(self, connector: S1) -> Connector where - U1: AsyncRead + AsyncWrite + Unpin + fmt::Debug, - T1: Service< + Io1: ConnectionIo + fmt::Debug, + S1: Service< TcpConnect, - Response = TcpConnection, - Error = actix_tls::connect::ConnectError, + Response = TcpConnection, + Error = TcpConnectError, > + Clone, { Connector { @@ -131,23 +134,30 @@ impl Connector { } } -impl Connector +impl Connector where - U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, - T: Service< + Io: ConnectionIo + fmt::Debug, + S: Service< TcpConnect, - Response = TcpConnection, - Error = actix_tls::connect::ConnectError, + Response = TcpConnection, + Error = TcpConnectError, > + Clone + 'static, { - /// Connection timeout, i.e. max time to connect to remote host including dns name resolution. - /// Set to 5 second by default. + /// Tcp connection timeout, i.e. max time to connect to remote host including dns name + /// resolution. Set to 5 second by default. pub fn timeout(mut self, timeout: Duration) -> Self { self.config.timeout = timeout; self } + /// Tls handshake timeout, i.e. max time to do tls handshake with remote host after tcp + /// connection established. Set to 5 second by default. + pub fn handshake_timeout(mut self, timeout: Duration) -> Self { + self.config.handshake_timeout = timeout; + self + } + #[cfg(feature = "openssl")] /// Use custom `SslConnector` instance. pub fn ssl(mut self, connector: OpensslConnector) -> Self { @@ -157,7 +167,7 @@ where #[cfg(feature = "rustls")] /// Use custom `SslConnector` instance. - pub fn rustls(mut self, connector: Arc) -> Self { + pub fn rustls(mut self, connector: std::sync::Arc) -> Self { self.ssl = SslConnector::Rustls(connector); self } @@ -247,158 +257,369 @@ where /// Finish configuration process and create connector service. /// The Connector builder always concludes by calling `finish()` last in /// its combinator chain. - pub fn finish( - self, - ) -> impl Service { + pub fn finish(self) -> ConnectorService { let local_address = self.config.local_address; let timeout = self.config.timeout; - let tcp_service = TimeoutService::new( - timeout, - apply_fn(self.connector.clone(), move |msg: Connect, srv| { - let mut req = TcpConnect::new(msg.uri).set_addr(msg.addr); + let tcp_service_inner = + TcpConnectorInnerService::new(self.connector, timeout, local_address); - if let Some(local_addr) = local_address { - req = req.set_local_addr(local_addr); + #[allow(clippy::redundant_clone)] + let tcp_service = TcpConnectorService { + service: tcp_service_inner.clone(), + }; + + let tls_service = match self.ssl { + SslConnector::None => None, + #[cfg(feature = "openssl")] + SslConnector::Openssl(tls) => { + const H2: &[u8] = b"h2"; + + use actix_tls::connect::ssl::openssl::{OpensslConnector, SslStream}; + + impl IntoConnectionIo for TcpConnection> { + fn into_connection_io(self) -> (Box, Protocol) { + let sock = self.into_parts().0; + let h2 = sock + .ssl() + .selected_alpn_protocol() + .map(|protos| protos.windows(2).any(|w| w == H2)) + .unwrap_or(false); + if h2 { + (Box::new(sock), Protocol::Http2) + } else { + (Box::new(sock), Protocol::Http1) + } + } } - srv.call(req) - }) - .map_err(ConnectError::from) - .map(|stream| (stream.into_parts().0, Protocol::Http1)), - ) - .map_err(|e| match e { - TimeoutError::Service(e) => e, - TimeoutError::Timeout => ConnectError::Timeout, - }); + let handshake_timeout = self.config.handshake_timeout; - #[cfg(not(any(feature = "openssl", feature = "rustls")))] - { - // A dummy service for annotate tls pool's type signature. - pub type DummyService = Box< - dyn Service< - Connect, - Response = (Box, Protocol), - Error = ConnectError, - Future = futures_core::future::LocalBoxFuture< - 'static, - Result<(Box, Protocol), ConnectError>, - >, - >, - >; + let tls_service = TlsConnectorService { + tcp_service: tcp_service_inner, + tls_service: OpensslConnector::service(tls), + timeout: handshake_timeout, + }; - InnerConnector::<_, DummyService, _, Box> { - tcp_pool: ConnectionPool::new( - tcp_service, - self.config.no_disconnect_timeout(), - ), - tls_pool: None, + Some(actix_service::boxed::rc_service(tls_service)) } - } - - #[cfg(any(feature = "openssl", feature = "rustls"))] - { - const H2: &[u8] = b"h2"; - use actix_service::{boxed::service, pipeline}; - #[cfg(feature = "openssl")] - use actix_tls::connect::ssl::openssl::OpensslConnector; #[cfg(feature = "rustls")] - use actix_tls::connect::ssl::rustls::{RustlsConnector, Session}; + SslConnector::Rustls(tls) => { + const H2: &[u8] = b"h2"; - let ssl_service = TimeoutService::new( - timeout, - pipeline( - apply_fn(self.connector.clone(), move |msg: Connect, srv| { - let mut req = TcpConnect::new(msg.uri).set_addr(msg.addr); + use actix_tls::connect::ssl::rustls::{ + RustlsConnector, Session, TlsStream, + }; - if let Some(local_addr) = local_address { - req = req.set_local_addr(local_addr); + impl IntoConnectionIo for TcpConnection> { + fn into_connection_io(self) -> (Box, Protocol) { + let sock = self.into_parts().0; + let h2 = sock + .get_ref() + .1 + .get_alpn_protocol() + .map(|protos| protos.windows(2).any(|w| w == H2)) + .unwrap_or(false); + if h2 { + (Box::new(sock), Protocol::Http2) + } else { + (Box::new(sock), Protocol::Http1) } + } + } - srv.call(req) - }) - .map_err(ConnectError::from), - ) - .and_then(match self.ssl { - #[cfg(feature = "openssl")] - SslConnector::Openssl(ssl) => service( - OpensslConnector::service(ssl) - .map(|stream| { - let sock = stream.into_parts().0; - let h2 = sock - .ssl() - .selected_alpn_protocol() - .map(|protos| protos.windows(2).any(|w| w == H2)) - .unwrap_or(false); - if h2 { - ( - Box::new(sock) as Box, - Protocol::Http2, - ) - } else { - (Box::new(sock) as _, Protocol::Http1) - } - }) - .map_err(ConnectError::from), - ), - #[cfg(feature = "rustls")] - SslConnector::Rustls(ssl) => service( - RustlsConnector::service(ssl) - .map_err(ConnectError::from) - .map(|stream| { - let sock = stream.into_parts().0; - let h2 = sock - .get_ref() - .1 - .get_alpn_protocol() - .map(|protos| protos.windows(2).any(|w| w == H2)) - .unwrap_or(false); - if h2 { - (Box::new(sock) as _, Protocol::Http2) - } else { - (Box::new(sock) as _, Protocol::Http1) - } - }), - ), - }), - ) - .map_err(|e| match e { - TimeoutError::Service(e) => e, - TimeoutError::Timeout => ConnectError::Timeout, - }); + let handshake_timeout = self.config.handshake_timeout; - InnerConnector { - tcp_pool: ConnectionPool::new( - tcp_service, - self.config.no_disconnect_timeout(), - ), - tls_pool: Some(ConnectionPool::new(ssl_service, self.config)), + let tls_service = TlsConnectorService { + tcp_service: tcp_service_inner, + tls_service: RustlsConnector::service(tls), + timeout: handshake_timeout, + }; + + Some(actix_service::boxed::rc_service(tls_service)) } + }; + + let tcp_config = self.config.no_disconnect_timeout(); + + let tcp_pool = ConnectionPool::new(tcp_service, tcp_config); + + let tls_config = self.config; + let tls_pool = tls_service + .map(move |tls_service| ConnectionPool::new(tls_service, tls_config)); + + ConnectorServicePriv { tcp_pool, tls_pool } + } +} + +/// tcp service for map `TcpConnection` type to `(Io, Protocol)` +#[derive(Clone)] +pub struct TcpConnectorService { + service: S, +} + +impl Service for TcpConnectorService +where + S: Service, Error = ConnectError> + + Clone + + 'static, +{ + type Response = (Io, Protocol); + type Error = ConnectError; + type Future = TcpConnectorFuture; + + actix_service::forward_ready!(service); + + fn call(&self, req: Connect) -> Self::Future { + TcpConnectorFuture { + fut: self.service.call(req), } } } -struct InnerConnector +#[pin_project] +pub struct TcpConnectorFuture { + #[pin] + fut: Fut, +} + +impl Future for TcpConnectorFuture where - S1: Service + 'static, - S2: Service + 'static, - Io1: AsyncRead + AsyncWrite + Unpin + 'static, - Io2: AsyncRead + AsyncWrite + Unpin + 'static, + Fut: Future, ConnectError>>, +{ + type Output = Result<(Io, Protocol), ConnectError>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.project() + .fut + .poll(cx) + .map_ok(|res| (res.into_parts().0, Protocol::Http1)) + } +} + +/// service for establish tcp connection and do client tls handshake. +/// operation is canceled when timeout limit reached. +struct TlsConnectorService { + /// tcp connection is canceled on `TcpConnectorInnerService`'s timeout setting. + tcp_service: S, + /// tls connection is canceled on `TlsConnectorService`'s timeout setting. + tls_service: St, + timeout: Duration, +} + +impl Service for TlsConnectorService +where + S: Service, Error = ConnectError> + + Clone + + 'static, + St: Service, Response = Res, Error = std::io::Error> + + Clone + + 'static, + Io: ConnectionIo, + Res: IntoConnectionIo, +{ + type Response = (Box, Protocol); + type Error = ConnectError; + type Future = TlsConnectorFuture; + + fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { + ready!(self.tcp_service.poll_ready(cx))?; + ready!(self.tls_service.poll_ready(cx))?; + Poll::Ready(Ok(())) + } + + fn call(&self, req: Connect) -> Self::Future { + let fut = self.tcp_service.call(req); + let tls_service = self.tls_service.clone(); + let timeout = self.timeout; + + TlsConnectorFuture::TcpConnect { + fut, + tls_service: Some(tls_service), + timeout, + } + } +} + +#[pin_project(project = TlsConnectorProj)] +#[allow(clippy::large_enum_variant)] +enum TlsConnectorFuture { + TcpConnect { + #[pin] + fut: Fut1, + tls_service: Option, + timeout: Duration, + }, + TlsConnect { + #[pin] + fut: Fut2, + #[pin] + timeout: Sleep, + }, +} + +/// helper trait for generic over different TlsStream types between tls crates. +trait IntoConnectionIo { + fn into_connection_io(self) -> (Box, Protocol); +} + +impl Future for TlsConnectorFuture +where + S: Service< + TcpConnection, + Response = Res, + Error = std::io::Error, + Future = Fut2, + >, + Fut1: Future, ConnectError>>, + Fut2: Future>, + Io: ConnectionIo, + Res: IntoConnectionIo, +{ + type Output = Result<(Box, Protocol), ConnectError>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.as_mut().project() { + TlsConnectorProj::TcpConnect { + fut, + tls_service, + timeout, + } => { + let res = ready!(fut.poll(cx))?; + let fut = tls_service + .take() + .expect("TlsConnectorFuture polled after complete") + .call(res); + let timeout = sleep(*timeout); + self.set(TlsConnectorFuture::TlsConnect { fut, timeout }); + self.poll(cx) + } + TlsConnectorProj::TlsConnect { fut, timeout } => match fut.poll(cx)? { + Poll::Ready(res) => Poll::Ready(Ok(res.into_connection_io())), + Poll::Pending => timeout.poll(cx).map(|_| Err(ConnectError::Timeout)), + }, + } + } +} + +/// service for establish tcp connection. +/// operation is canceled when timeout limit reached. +#[derive(Clone)] +pub struct TcpConnectorInnerService { + service: S, + timeout: Duration, + local_address: Option, +} + +impl TcpConnectorInnerService { + fn new( + service: S, + timeout: Duration, + local_address: Option, + ) -> Self { + Self { + service, + timeout, + local_address, + } + } +} + +impl Service for TcpConnectorInnerService +where + S: Service< + TcpConnect, + Response = TcpConnection, + Error = TcpConnectError, + > + Clone + + 'static, +{ + type Response = S::Response; + type Error = ConnectError; + type Future = TcpConnectorInnerFuture; + + actix_service::forward_ready!(service); + + fn call(&self, req: Connect) -> Self::Future { + let mut req = TcpConnect::new(req.uri).set_addr(req.addr); + + if let Some(local_addr) = self.local_address { + req = req.set_local_addr(local_addr); + } + + TcpConnectorInnerFuture { + fut: self.service.call(req), + timeout: sleep(self.timeout), + } + } +} + +#[pin_project] +pub struct TcpConnectorInnerFuture { + #[pin] + fut: Fut, + #[pin] + timeout: Sleep, +} + +impl Future for TcpConnectorInnerFuture +where + Fut: Future, TcpConnectError>>, +{ + type Output = Result, ConnectError>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + match this.fut.poll(cx) { + Poll::Ready(res) => Poll::Ready(res.map_err(ConnectError::from)), + Poll::Pending => this.timeout.poll(cx).map(|_| Err(ConnectError::Timeout)), + } + } +} + +/// Connector service for pooled Plain/Tls Tcp connections. +pub type ConnectorService = ConnectorServicePriv< + TcpConnectorService>, + Rc< + dyn Service< + Connect, + Response = (Box, Protocol), + Error = ConnectError, + Future = LocalBoxFuture< + 'static, + Result<(Box, Protocol), ConnectError>, + >, + >, + >, + Io, + Box, +>; + +pub struct ConnectorServicePriv +where + S1: Service, + S2: Service, + Io1: ConnectionIo, + Io2: ConnectionIo, { tcp_pool: ConnectionPool, tls_pool: Option>, } -impl Service for InnerConnector +impl Service for ConnectorServicePriv where - S1: Service + 'static, - S2: Service + 'static, - Io1: AsyncRead + AsyncWrite + Unpin + 'static, - Io2: AsyncRead + AsyncWrite + Unpin + 'static, + S1: Service + + Clone + + 'static, + S2: Service + + Clone + + 'static, + Io1: ConnectionIo, + Io2: ConnectionIo, { - type Response = EitherIoConnection; + type Response = Connection; type Error = ConnectError; - type Future = InnerConnectorResponse; + type Future = ConnectorServiceFuture; fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { ready!(self.tcp_pool.poll_ready(cx))?; @@ -411,41 +632,49 @@ where fn call(&self, req: Connect) -> Self::Future { match req.uri.scheme_str() { Some("https") | Some("wss") => match self.tls_pool { - None => InnerConnectorResponse::SslIsNotSupported, - Some(ref pool) => InnerConnectorResponse::Io2(pool.call(req)), + None => ConnectorServiceFuture::SslIsNotSupported, + Some(ref pool) => ConnectorServiceFuture::Tls(pool.call(req)), }, - _ => InnerConnectorResponse::Io1(self.tcp_pool.call(req)), + _ => ConnectorServiceFuture::Tcp(self.tcp_pool.call(req)), } } } -#[pin_project::pin_project(project = InnerConnectorProj)] -enum InnerConnectorResponse +#[pin_project(project = ConnectorServiceProj)] +pub enum ConnectorServiceFuture where - S1: Service + 'static, - S2: Service + 'static, - Io1: AsyncRead + AsyncWrite + Unpin + 'static, - Io2: AsyncRead + AsyncWrite + Unpin + 'static, + S1: Service + + Clone + + 'static, + S2: Service + + Clone + + 'static, + Io1: ConnectionIo, + Io2: ConnectionIo, { - Io1(#[pin] as Service>::Future), - Io2(#[pin] as Service>::Future), + Tcp(#[pin] as Service>::Future), + Tls(#[pin] as Service>::Future), SslIsNotSupported, } -impl Future for InnerConnectorResponse +impl Future for ConnectorServiceFuture where - S1: Service + 'static, - S2: Service + 'static, - Io1: AsyncRead + AsyncWrite + Unpin + 'static, - Io2: AsyncRead + AsyncWrite + Unpin + 'static, + S1: Service + + Clone + + 'static, + S2: Service + + Clone + + 'static, + Io1: ConnectionIo, + Io2: ConnectionIo, { - type Output = Result, ConnectError>; + type Output = Result, ConnectError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.project() { - InnerConnectorProj::Io1(fut) => fut.poll(cx).map_ok(EitherIoConnection::A), - InnerConnectorProj::Io2(fut) => fut.poll(cx).map_ok(EitherIoConnection::B), - InnerConnectorProj::SslIsNotSupported => { + ConnectorServiceProj::Tcp(fut) => fut.poll(cx).map_ok(Connection::Tcp), + ConnectorServiceProj::Tls(fut) => fut.poll(cx).map_ok(Connection::Tls), + ConnectorServiceProj::SslIsNotSupported => { Poll::Ready(Err(ConnectError::SslIsNotSupported)) } } diff --git a/actix-http/src/client/h1proto.rs b/actix-http/src/client/h1proto.rs index 75a3c1f3d..01a6e1edf 100644 --- a/actix-http/src/client/h1proto.rs +++ b/actix-http/src/client/h1proto.rs @@ -1,9 +1,10 @@ -use std::io::Write; -use std::pin::Pin; -use std::task::{Context, Poll}; -use std::{io, time}; +use std::{ + io::Write, + pin::Pin, + task::{Context, Poll}, +}; -use actix_codec::{AsyncRead, AsyncWrite, Framed, ReadBuf}; +use actix_codec::Framed; use bytes::buf::BufMut; use bytes::{Bytes, BytesMut}; use futures_core::Stream; @@ -11,28 +12,24 @@ use futures_util::{future::poll_fn, SinkExt as _}; use crate::error::PayloadError; use crate::h1; -use crate::header::HeaderMap; use crate::http::{ - header::{IntoHeaderValue, EXPECT, HOST}, + header::{HeaderMap, IntoHeaderValue, EXPECT, HOST}, StatusCode, }; use crate::message::{RequestHeadType, ResponseHead}; use crate::payload::{Payload, PayloadStream}; -use super::connection::ConnectionType; +use super::connection::{ConnectionIo, H1Connection}; use super::error::{ConnectError, SendRequestError}; -use super::pool::Acquired; use crate::body::{BodySize, MessageBody}; -pub(crate) async fn send_request( - io: T, +pub(crate) async fn send_request( + io: H1Connection, mut head: RequestHeadType, body: B, - created: time::Instant, - acquired: Acquired, ) -> Result<(ResponseHead, Payload), SendRequestError> where - T: AsyncRead + AsyncWrite + Unpin + 'static, + Io: ConnectionIo, B: MessageBody, { // set request host header @@ -62,12 +59,6 @@ where } } - let io = H1Connection { - created, - acquired, - io: Some(io), - }; - // create Framed and prepare sending request let mut framed = Framed::new(io, h1::ClientCodec::default()); @@ -138,18 +129,18 @@ where } } -pub(crate) async fn open_tunnel( - io: T, +pub(crate) async fn open_tunnel( + io: Io, head: RequestHeadType, -) -> Result<(ResponseHead, Framed), SendRequestError> +) -> Result<(ResponseHead, Framed), SendRequestError> where - T: AsyncRead + AsyncWrite + Unpin + 'static, + Io: ConnectionIo, { - // create Framed and send request + // create Framed and send request. let mut framed = Framed::new(io, h1::ClientCodec::default()); framed.send((head, BodySize::None).into()).await?; - // read response + // read response head. let head = poll_fn(|cx| Pin::new(&mut framed).poll_next(cx)) .await .ok_or(ConnectError::Disconnected)??; @@ -158,12 +149,12 @@ where } /// send request body to the peer -pub(crate) async fn send_body( +pub(crate) async fn send_body( body: B, - mut framed: Pin<&mut Framed>, + mut framed: Pin<&mut Framed>, ) -> Result<(), SendRequestError> where - T: AsyncRead + AsyncWrite + Unpin + 'static, + Io: ConnectionIo, B: MessageBody, { actix_rt::pin!(body); @@ -202,92 +193,16 @@ where Ok(()) } -#[doc(hidden)] -/// HTTP client connection -pub struct H1Connection -where - T: AsyncRead + AsyncWrite + Unpin + 'static, -{ - /// T should be `Unpin` - io: Option, - created: time::Instant, - acquired: Acquired, -} - -impl H1Connection -where - T: AsyncRead + AsyncWrite + Unpin + 'static, -{ - fn on_release(&mut self, keep_alive: bool) { - if keep_alive { - self.release(); - } else { - self.close(); - } - } - - /// Close connection - fn close(&mut self) { - if let Some(io) = self.io.take() { - self.acquired.close(ConnectionType::H1(io)); - } - } - - /// Release this connection to the connection pool - fn release(&mut self) { - if let Some(io) = self.io.take() { - self.acquired.release(ConnectionType::H1(io), self.created); - } - } -} - -impl AsyncRead for H1Connection { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - Pin::new(&mut self.io.as_mut().unwrap()).poll_read(cx, buf) - } -} - -impl AsyncWrite for H1Connection { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - Pin::new(&mut self.io.as_mut().unwrap()).poll_write(cx, buf) - } - - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - Pin::new(self.io.as_mut().unwrap()).poll_flush(cx) - } - - fn poll_shutdown( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { - Pin::new(self.io.as_mut().unwrap()).poll_shutdown(cx) - } -} - #[pin_project::pin_project] -pub(crate) struct PlStream +pub(crate) struct PlStream where - Io: AsyncRead + AsyncWrite + Unpin + 'static, + Io: ConnectionIo, { #[pin] framed: Option, h1::ClientPayloadCodec>>, } -impl PlStream -where - Io: AsyncRead + AsyncWrite + Unpin + 'static, -{ +impl PlStream { fn new(framed: Framed, h1::ClientCodec>) -> Self { let framed = framed.into_map_codec(|codec| codec.into_payload_codec()); @@ -297,10 +212,7 @@ where } } -impl Stream for PlStream -where - Io: AsyncRead + AsyncWrite + Unpin + 'static, -{ +impl Stream for PlStream { type Item = Result; fn poll_next( diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index 82e81c7ff..437b9ae76 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -1,7 +1,5 @@ use std::future::Future; -use std::time; -use actix_codec::{AsyncRead, AsyncWrite}; use bytes::Bytes; use futures_util::future::poll_fn; use h2::{ @@ -17,20 +15,16 @@ use crate::message::{RequestHeadType, ResponseHead}; use crate::payload::Payload; use super::config::ConnectorConfig; -use super::connection::ConnectionType; +use super::connection::{ConnectionIo, H2Connection}; use super::error::SendRequestError; -use super::pool::Acquired; -use crate::client::connection::H2Connection; -pub(crate) async fn send_request( - mut io: H2Connection, +pub(crate) async fn send_request( + mut io: H2Connection, head: RequestHeadType, body: B, - created: time::Instant, - acquired: Acquired, ) -> Result<(ResponseHead, Payload), SendRequestError> where - T: AsyncRead + AsyncWrite + Unpin + 'static, + Io: ConnectionIo, B: MessageBody, { trace!("Sending client request: {:?} {:?}", head, body.size()); @@ -103,13 +97,13 @@ where let res = poll_fn(|cx| io.poll_ready(cx)).await; if let Err(e) = res { - release(io, acquired, created, e.is_io()); + io.on_release(e.is_io()); return Err(SendRequestError::from(e)); } let resp = match io.send_request(req, eof) { Ok((fut, send)) => { - release(io, acquired, created, false); + io.on_release(false); if !eof { send_body(body, send).await?; @@ -117,7 +111,7 @@ where fut.await.map_err(SendRequestError::from)? } Err(e) => { - release(io, acquired, created, e.is_io()); + io.on_release(e.is_io()); return Err(e.into()); } }; @@ -178,26 +172,10 @@ async fn send_body( } } -/// release SendRequest object -fn release( - io: H2Connection, - acquired: Acquired, - created: time::Instant, - close: bool, -) { - if close { - acquired.close(ConnectionType::H2(io)); - } else { - acquired.release(ConnectionType::H2(io), created); - } -} - -pub(crate) fn handshake( +pub(crate) fn handshake( io: Io, config: &ConnectorConfig, ) -> impl Future, Connection), h2::Error>> -where - Io: AsyncRead + AsyncWrite + Unpin + 'static, { let mut builder = Builder::new(); builder diff --git a/actix-http/src/client/mod.rs b/actix-http/src/client/mod.rs index ce6aa3bc1..41d5fef2a 100644 --- a/actix-http/src/client/mod.rs +++ b/actix-http/src/client/mod.rs @@ -15,7 +15,7 @@ pub use actix_tls::connect::{ }; pub use self::connection::{Connection, ConnectionIo}; -pub use self::connector::Connector; +pub use self::connector::{Connector, ConnectorService}; pub use self::error::{ConnectError, FreezeRequestError, InvalidUrl, SendRequestError}; pub use crate::Protocol; diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index 79adcf3e2..88188038f 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -1,34 +1,38 @@ //! Client connection pooling keyed on the authority part of the connection URI. -use std::collections::VecDeque; -use std::future::Future; -use std::ops::Deref; -use std::pin::Pin; -use std::rc::Rc; -use std::sync::Arc; -use std::task::{Context, Poll}; -use std::time::{Duration, Instant}; -use std::{cell::RefCell, io}; +use std::{ + cell::RefCell, + collections::VecDeque, + future::Future, + io, + ops::Deref, + pin::Pin, + rc::Rc, + sync::Arc, + task::{Context, Poll}, + time::{Duration, Instant}, +}; -use actix_codec::{AsyncRead, AsyncWrite}; +use actix_codec::{AsyncRead, AsyncWrite, ReadBuf}; use actix_rt::time::{sleep, Sleep}; use actix_service::Service; use ahash::AHashMap; use futures_core::future::LocalBoxFuture; use http::uri::Authority; use pin_project::pin_project; -use tokio::io::ReadBuf; use tokio::sync::{OwnedSemaphorePermit, Semaphore}; use super::config::ConnectorConfig; -use super::connection::{ConnectionType, H2Connection, IoConnection}; +use super::connection::{ + ConnectionInnerType, ConnectionIo, ConnectionType, H2ConnectionInner, +}; use super::error::ConnectError; use super::h2proto::handshake; use super::Connect; use super::Protocol; #[derive(Hash, Eq, PartialEq, Clone, Debug)] -pub(crate) struct Key { +pub struct Key { authority: Authority, } @@ -38,17 +42,18 @@ impl From for Key { } } +#[doc(hidden)] /// Connections pool for reuse Io type for certain [`http::uri::Authority`] as key. -pub(crate) struct ConnectionPool +pub struct ConnectionPool where Io: AsyncWrite + Unpin + 'static, { - connector: Rc, + connector: S, inner: ConnectionPoolInner, } /// wrapper type for check the ref count of Rc. -struct ConnectionPoolInner(Rc>) +pub struct ConnectionPoolInner(Rc>) where Io: AsyncWrite + Unpin + 'static; @@ -56,10 +61,21 @@ impl ConnectionPoolInner where Io: AsyncWrite + Unpin + 'static, { + fn new(config: ConnectorConfig) -> Self { + let permits = Arc::new(Semaphore::new(config.limit)); + let available = RefCell::new(AHashMap::default()); + + Self(Rc::new(ConnectionPoolInnerPriv { + config, + available, + permits, + })) + } + /// spawn a async for graceful shutdown h1 Io type with a timeout. - fn close(&self, conn: ConnectionType) { + fn close(&self, conn: ConnectionInnerType) { if let Some(timeout) = self.config.disconnect_timeout { - if let ConnectionType::H1(io) = conn { + if let ConnectionInnerType::H1(io) = conn { actix_rt::spawn(CloseConnection::new(io, timeout)); } } @@ -104,7 +120,7 @@ where } } -struct ConnectionPoolInnerPriv +pub struct ConnectionPoolInnerPriv where Io: AsyncWrite + Unpin + 'static, { @@ -128,15 +144,7 @@ where /// Any requests beyond limit would be wait in fifo order and get notified in async manner /// by [`tokio::sync::Semaphore`] pub(crate) fn new(connector: S, config: ConnectorConfig) -> Self { - let permits = Arc::new(Semaphore::new(config.limit)); - let available = RefCell::new(AHashMap::default()); - let connector = Rc::new(connector); - - let inner = ConnectionPoolInner(Rc::new(ConnectionPoolInnerPriv { - config, - available, - permits, - })); + let inner = ConnectionPoolInner::new(config); Self { connector, inner } } @@ -144,12 +152,14 @@ where impl Service for ConnectionPool where - S: Service + 'static, - Io: AsyncRead + AsyncWrite + Unpin + 'static, + S: Service + + Clone + + 'static, + Io: ConnectionIo, { - type Response = IoConnection; + type Response = ConnectionType; type Error = ConnectError; - type Future = LocalBoxFuture<'static, Result, ConnectError>>; + type Future = LocalBoxFuture<'static, Result>; actix_service::forward_ready!(connector); @@ -193,7 +203,7 @@ where inner.close(c.conn); } else { // check if the connection is still usable - if let ConnectionType::H1(ref mut io) = c.conn { + if let ConnectionInnerType::H1(ref mut io) = c.conn { let check = ConnectionCheckFuture { io }; match check.await { ConnectionState::Tainted => { @@ -221,7 +231,9 @@ where // match the connection and spawn new one if did not get anything. match conn { - Some(conn) => Ok(IoConnection::new(conn.conn, conn.created, acquired)), + Some(conn) => { + Ok(ConnectionType::from_pool(conn.conn, conn.created, acquired)) + } None => { let (io, proto) = connector.call(req).await?; @@ -229,19 +241,12 @@ where assert!(proto != Protocol::Http3); if proto == Protocol::Http1 { - Ok(IoConnection::new( - ConnectionType::H1(io), - Instant::now(), - acquired, - )) + Ok(ConnectionType::from_h1(io, Instant::now(), acquired)) } else { let config = &acquired.inner.config; let (sender, connection) = handshake(io, config).await?; - Ok(IoConnection::new( - ConnectionType::H2(H2Connection::new(sender, connection)), - Instant::now(), - acquired, - )) + let inner = H2ConnectionInner::new(sender, connection); + Ok(ConnectionType::from_h2(inner, Instant::now(), acquired)) } } } @@ -292,7 +297,7 @@ where } struct PooledConnection { - conn: ConnectionType, + conn: ConnectionInnerType, used: Instant, created: Instant, } @@ -332,26 +337,26 @@ where } } -pub(crate) struct Acquired +pub struct Acquired where Io: AsyncWrite + Unpin + 'static, { + /// authority key for identify connection. key: Key, + /// handle to connection pool. inner: ConnectionPoolInner, + /// permit for limit concurrent in-flight connection for a Client object. permit: OwnedSemaphorePermit, } -impl Acquired -where - Io: AsyncRead + AsyncWrite + Unpin + 'static, -{ +impl Acquired { /// Close the IO. - pub(crate) fn close(&self, conn: ConnectionType) { + pub(super) fn close(&self, conn: ConnectionInnerType) { self.inner.close(conn); } /// Release IO back into pool. - pub(crate) fn release(&self, conn: ConnectionType, created: Instant) { + pub(super) fn release(&self, conn: ConnectionInnerType, created: Instant) { let Acquired { key, inner, .. } = self; inner @@ -376,7 +381,7 @@ mod test { use http::Uri; use super::*; - use crate::client::connection::IoConnection; + use crate::client::connection::ConnectionType; /// A stream type that always returns pending on async read. /// @@ -423,6 +428,7 @@ mod test { } } + #[derive(Clone)] struct TestPoolConnector { generated: Rc>, } @@ -441,12 +447,14 @@ mod test { } } - fn release(conn: IoConnection) + fn release(conn: ConnectionType) where T: AsyncRead + AsyncWrite + Unpin + 'static, { - let (conn, created, acquired) = conn.into_parts(); - acquired.release(conn, created); + match conn { + ConnectionType::H1(mut conn) => conn.on_release(true), + ConnectionType::H2(mut conn) => conn.on_release(false), + } } #[actix_rt::test] diff --git a/awc/CHANGES.md b/awc/CHANGES.md index bf7bffc49..d2cb7c009 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -1,6 +1,10 @@ # Changes ## Unreleased - 2021-xx-xx +### Changed +* `ConnectorService` type is renamed to `BoxConnectorService` [#2081] + +[#2081]: https://github.com/actix/actix-web/pull/2081 ## 3.0.0-beta.3 - 2021-03-08 diff --git a/awc/src/builder.rs b/awc/src/builder.rs index 33f43ab93..925d9ae2a 100644 --- a/awc/src/builder.rs +++ b/awc/src/builder.rs @@ -6,7 +6,7 @@ use std::time::Duration; use actix_codec::{AsyncRead, AsyncWrite}; use actix_http::{ - client::{Connector, TcpConnect, TcpConnectError, TcpConnection}, + client::{Connector, ConnectorService, TcpConnect, TcpConnectError, TcpConnection}, http::{self, header, Error as HttpError, HeaderMap, HeaderName, Uri}, }; use actix_rt::net::TcpStream; @@ -15,7 +15,7 @@ use actix_service::{boxed, Service}; use crate::connect::DefaultConnector; use crate::error::SendRequestError; use crate::middleware::{NestTransform, Redirect, Transform}; -use crate::{Client, ClientConfig, ConnectRequest, ConnectResponse, ConnectorService}; +use crate::{Client, ClientConfig, ConnectRequest, ConnectResponse}; /// An HTTP Client builder /// @@ -234,7 +234,7 @@ where /// Finish build process and create `Client` instance. pub fn finish(self) -> Client where - M: Transform + 'static, + M: Transform>, ConnectRequest> + 'static, M::Transform: Service, { @@ -250,7 +250,7 @@ where fn _finish(self) -> Client where - M: Transform + 'static, + M: Transform>, ConnectRequest> + 'static, M::Transform: Service, { @@ -269,16 +269,14 @@ where connector = connector.local_address(val); } - let connector = boxed::service(DefaultConnector::new(connector.finish())); - let connector = boxed::service(self.middleware.new_transform(connector)); + let connector = DefaultConnector::new(connector.finish()); + let connector = boxed::rc_service(self.middleware.new_transform(connector)); - let config = ClientConfig { - headers: self.headers, + Client(ClientConfig { + headers: Rc::new(self.headers), timeout: self.timeout, connector, - }; - - Client(Rc::new(config)) + }) } } diff --git a/awc/src/connect.rs b/awc/src/connect.rs index d3bc01ed6..6a9fc4630 100644 --- a/awc/src/connect.rs +++ b/awc/src/connect.rs @@ -2,6 +2,7 @@ use std::{ future::Future, net, pin::Pin, + rc::Rc, task::{Context, Poll}, }; @@ -19,7 +20,7 @@ use futures_core::{future::LocalBoxFuture, ready}; use crate::response::ClientResponse; -pub type ConnectorService = Box< +pub type BoxConnectorService = Rc< dyn Service< ConnectRequest, Response = ConnectResponse, @@ -28,6 +29,8 @@ pub type ConnectorService = Box< >, >; +pub type BoxedSocket = Box; + pub enum ConnectRequest { Client(RequestHeadType, Body, Option), Tunnel(RequestHead, Option), @@ -58,7 +61,7 @@ impl ConnectResponse { } } -pub(crate) struct DefaultConnector { +pub struct DefaultConnector { connector: S, } @@ -68,15 +71,14 @@ impl DefaultConnector { } } -impl Service for DefaultConnector +impl Service for DefaultConnector where - S: Service, - S::Response: Connection, - ::Io: 'static, + S: Service>, + Io: ConnectionIo, { type Response = ConnectResponse; type Error = SendRequestError; - type Future = ConnectRequestFuture::Io>; + type Future = ConnectRequestFuture; actix_service::forward_ready!(connector); @@ -102,7 +104,10 @@ where pin_project_lite::pin_project! { #[project = ConnectRequestProj] - pub(crate) enum ConnectRequestFuture { + pub enum ConnectRequestFuture + where + Io: ConnectionIo + { Connection { #[pin] fut: Fut, @@ -114,16 +119,15 @@ pin_project_lite::pin_project! { Tunnel { fut: LocalBoxFuture< 'static, - Result<(ResponseHead, Framed), SendRequestError>, + Result<(ResponseHead, Framed, ClientCodec>), SendRequestError>, >, } } } -impl Future for ConnectRequestFuture +impl Future for ConnectRequestFuture where - Fut: Future>, - C: Connection, + Fut: Future, ConnectError>>, Io: ConnectionIo, { type Output = Result; @@ -165,5 +169,3 @@ where } } } - -pub type BoxedSocket = Box; diff --git a/awc/src/frozen.rs b/awc/src/frozen.rs index 46b4063a0..5fe8edb19 100644 --- a/awc/src/frozen.rs +++ b/awc/src/frozen.rs @@ -23,7 +23,7 @@ pub struct FrozenClientRequest { pub(crate) addr: Option, pub(crate) response_decompress: bool, pub(crate) timeout: Option, - pub(crate) config: Rc, + pub(crate) config: ClientConfig, } impl FrozenClientRequest { @@ -51,7 +51,7 @@ impl FrozenClientRequest { self.addr, self.response_decompress, self.timeout, - self.config.as_ref(), + &self.config, body, ) } @@ -62,7 +62,7 @@ impl FrozenClientRequest { self.addr, self.response_decompress, self.timeout, - self.config.as_ref(), + &self.config, value, ) } @@ -73,7 +73,7 @@ impl FrozenClientRequest { self.addr, self.response_decompress, self.timeout, - self.config.as_ref(), + &self.config, value, ) } @@ -88,7 +88,7 @@ impl FrozenClientRequest { self.addr, self.response_decompress, self.timeout, - self.config.as_ref(), + &self.config, stream, ) } @@ -99,7 +99,7 @@ impl FrozenClientRequest { self.addr, self.response_decompress, self.timeout, - self.config.as_ref(), + &self.config, ) } @@ -168,7 +168,7 @@ impl FrozenSendBuilder { self.req.addr, self.req.response_decompress, self.req.timeout, - self.req.config.as_ref(), + &self.req.config, body, ) } @@ -183,7 +183,7 @@ impl FrozenSendBuilder { self.req.addr, self.req.response_decompress, self.req.timeout, - self.req.config.as_ref(), + &self.req.config, value, ) } @@ -198,7 +198,7 @@ impl FrozenSendBuilder { self.req.addr, self.req.response_decompress, self.req.timeout, - self.req.config.as_ref(), + &self.req.config, value, ) } @@ -217,7 +217,7 @@ impl FrozenSendBuilder { self.req.addr, self.req.response_decompress, self.req.timeout, - self.req.config.as_ref(), + &self.req.config, stream, ) } @@ -232,7 +232,7 @@ impl FrozenSendBuilder { self.req.addr, self.req.response_decompress, self.req.timeout, - self.req.config.as_ref(), + &self.req.config, ) } } diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 77e08fbbd..8addf4d8b 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -121,7 +121,7 @@ pub mod test; pub mod ws; pub use self::builder::ClientBuilder; -pub use self::connect::{BoxedSocket, ConnectRequest, ConnectResponse, ConnectorService}; +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}; @@ -147,11 +147,12 @@ pub use self::sender::SendClientRequest; /// } /// ``` #[derive(Clone)] -pub struct Client(Rc); +pub struct Client(ClientConfig); +#[derive(Clone)] pub(crate) struct ClientConfig { - pub(crate) connector: ConnectorService, - pub(crate) headers: HeaderMap, + pub(crate) connector: BoxConnectorService, + pub(crate) headers: Rc, pub(crate) timeout: Option, } diff --git a/awc/src/middleware/redirect.rs b/awc/src/middleware/redirect.rs index f8bdd2def..62ea1d0ac 100644 --- a/awc/src/middleware/redirect.rs +++ b/awc/src/middleware/redirect.rs @@ -189,7 +189,7 @@ where // remove body .call(ConnectRequest::Client(head, Body::None, addr)); - self.as_mut().set(RedirectServiceFuture::Client { + self.set(RedirectServiceFuture::Client { fut, max_redirect_times, uri: Some(uri), @@ -236,7 +236,7 @@ where .unwrap() .call(ConnectRequest::Client(head, body_new, addr)); - self.as_mut().set(RedirectServiceFuture::Client { + self.set(RedirectServiceFuture::Client { fut, max_redirect_times, uri: Some(uri), diff --git a/awc/src/request.rs b/awc/src/request.rs index 1b63f3687..3e9babb7d 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -57,7 +57,7 @@ pub struct ClientRequest { addr: Option, response_decompress: bool, timeout: Option, - config: Rc, + config: ClientConfig, #[cfg(feature = "cookies")] cookies: Option, @@ -65,7 +65,7 @@ pub struct ClientRequest { impl ClientRequest { /// Create new client request builder. - pub(crate) fn new(method: Method, uri: U, config: Rc) -> Self + pub(crate) fn new(method: Method, uri: U, config: ClientConfig) -> Self where Uri: TryFrom, >::Error: Into, @@ -398,7 +398,7 @@ impl ClientRequest { slf.addr, slf.response_decompress, slf.timeout, - slf.config.as_ref(), + &slf.config, body, ) } @@ -414,7 +414,7 @@ impl ClientRequest { slf.addr, slf.response_decompress, slf.timeout, - slf.config.as_ref(), + &slf.config, value, ) } @@ -432,7 +432,7 @@ impl ClientRequest { slf.addr, slf.response_decompress, slf.timeout, - slf.config.as_ref(), + &slf.config, value, ) } @@ -452,7 +452,7 @@ impl ClientRequest { slf.addr, slf.response_decompress, slf.timeout, - slf.config.as_ref(), + &slf.config, stream, ) } @@ -468,7 +468,7 @@ impl ClientRequest { slf.addr, slf.response_decompress, slf.timeout, - slf.config.as_ref(), + &slf.config, ) } diff --git a/awc/src/ws.rs b/awc/src/ws.rs index f64e9e19a..df25b7289 100644 --- a/awc/src/ws.rs +++ b/awc/src/ws.rs @@ -28,7 +28,6 @@ use std::convert::TryFrom; use std::net::SocketAddr; -use std::rc::Rc; use std::{fmt, str}; use actix_codec::Framed; @@ -56,7 +55,7 @@ pub struct WebsocketsRequest { addr: Option, max_size: usize, server_mode: bool, - config: Rc, + config: ClientConfig, #[cfg(feature = "cookies")] cookies: Option, @@ -64,7 +63,7 @@ pub struct WebsocketsRequest { impl WebsocketsRequest { /// Create new WebSocket connection - pub(crate) fn new(uri: U, config: Rc) -> Self + pub(crate) fn new(uri: U, config: ClientConfig) -> Self where Uri: TryFrom, >::Error: Into, From 81942d31d66b2a3fd4a5ed673ce31d21ae8ec5fa Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Fri, 19 Mar 2021 02:02:30 +0000 Subject: [PATCH 26/36] fix new dyn trait lint --- actix-http/src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index d3095e68d..d9b97d6a6 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -54,7 +54,7 @@ impl Error { /// Similar to `as_response_error` but downcasts. pub fn as_error(&self) -> Option<&T> { - ResponseError::downcast_ref(self.cause.as_ref()) + ::downcast_ref(self.cause.as_ref()) } } From 78fcd0237a4033e9802313de0299ce9cbb0a614c Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Fri, 19 Mar 2021 00:08:23 -0400 Subject: [PATCH 27/36] Format extract macro (#2087) --- src/extract.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/extract.rs b/src/extract.rs index 7a677bca4..921d9fc36 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -302,13 +302,13 @@ macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => { } )+ - if ready { - Poll::Ready(Ok( - ($(this.items.$n.take().unwrap(),)+) - )) - } else { - Poll::Pending - } + if ready { + Poll::Ready(Ok( + ($(this.items.$n.take().unwrap(),)+) + )) + } else { + Poll::Pending + } } } } @@ -318,16 +318,16 @@ macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => { mod m { use super::*; -tuple_from_req!(TupleFromRequest1, (0, A)); -tuple_from_req!(TupleFromRequest2, (0, A), (1, B)); -tuple_from_req!(TupleFromRequest3, (0, A), (1, B), (2, C)); -tuple_from_req!(TupleFromRequest4, (0, A), (1, B), (2, C), (3, D)); -tuple_from_req!(TupleFromRequest5, (0, A), (1, B), (2, C), (3, D), (4, E)); -tuple_from_req!(TupleFromRequest6, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F)); -tuple_from_req!(TupleFromRequest7, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G)); -tuple_from_req!(TupleFromRequest8, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H)); -tuple_from_req!(TupleFromRequest9, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I)); -tuple_from_req!(TupleFromRequest10, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J)); + tuple_from_req!(TupleFromRequest1, (0, A)); + tuple_from_req!(TupleFromRequest2, (0, A), (1, B)); + tuple_from_req!(TupleFromRequest3, (0, A), (1, B), (2, C)); + tuple_from_req!(TupleFromRequest4, (0, A), (1, B), (2, C), (3, D)); + tuple_from_req!(TupleFromRequest5, (0, A), (1, B), (2, C), (3, D), (4, E)); + tuple_from_req!(TupleFromRequest6, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F)); + tuple_from_req!(TupleFromRequest7, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G)); + tuple_from_req!(TupleFromRequest8, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H)); + tuple_from_req!(TupleFromRequest9, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I)); + tuple_from_req!(TupleFromRequest10, (0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J)); } #[cfg(test)] From 351286486c5b88d6fa879ba2d0ccc0335f41dfc1 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Fri, 19 Mar 2021 04:25:35 -0700 Subject: [PATCH 28/36] fix clippy warning on nightly (#2088) * fix clippy warning on nightly --- actix-files/src/service.rs | 2 +- actix-http/src/client/connection.rs | 2 +- actix-http/src/config.rs | 20 +++++--------------- actix-http/src/header/shared/entity.rs | 5 ++--- actix-http/src/header/shared/extended.rs | 2 +- src/config.rs | 2 +- src/lib.rs | 12 ++---------- src/middleware/compress.rs | 17 +++++++++-------- src/resource.rs | 2 +- src/test.rs | 2 +- tests/test_httpserver.rs | 16 ++++++---------- 11 files changed, 30 insertions(+), 52 deletions(-) diff --git a/actix-files/src/service.rs b/actix-files/src/service.rs index 14eea6ebc..3214963ed 100644 --- a/actix-files/src/service.rs +++ b/actix-files/src/service.rs @@ -1,4 +1,4 @@ -use std::{fmt, io, path::PathBuf, rc::Rc, task::Poll}; +use std::{fmt, io, path::PathBuf, rc::Rc}; use actix_service::Service; use actix_web::{ diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index 89dfd59de..78101397d 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -325,7 +325,7 @@ where } } -const H2_UNREACHABLE_WRITE: &'static str = "H2Connection can not impl AsyncWrite trait"; +const H2_UNREACHABLE_WRITE: &str = "H2Connection can not impl AsyncWrite trait"; impl AsyncWrite for Connection where diff --git a/actix-http/src/config.rs b/actix-http/src/config.rs index 9f84b8694..9a2293e92 100644 --- a/actix-http/src/config.rs +++ b/actix-http/src/config.rs @@ -126,9 +126,7 @@ impl ServiceConfig { pub fn client_timer(&self) -> Option { let delay_time = self.0.client_timeout; if delay_time != 0 { - Some(sleep_until( - self.0.date_service.now() + Duration::from_millis(delay_time), - )) + Some(sleep_until(self.now() + Duration::from_millis(delay_time))) } else { None } @@ -138,7 +136,7 @@ impl ServiceConfig { pub fn client_timer_expire(&self) -> Option { let delay = self.0.client_timeout; if delay != 0 { - Some(self.0.date_service.now() + Duration::from_millis(delay)) + Some(self.now() + Duration::from_millis(delay)) } else { None } @@ -148,7 +146,7 @@ impl ServiceConfig { pub fn client_disconnect_timer(&self) -> Option { let delay = self.0.client_disconnect; if delay != 0 { - Some(self.0.date_service.now() + Duration::from_millis(delay)) + Some(self.now() + Duration::from_millis(delay)) } else { None } @@ -157,20 +155,12 @@ impl ServiceConfig { #[inline] /// Return keep-alive timer delay is configured. pub fn keep_alive_timer(&self) -> Option { - if let Some(ka) = self.0.keep_alive { - Some(sleep_until(self.0.date_service.now() + ka)) - } else { - None - } + self.keep_alive().map(|ka| sleep_until(self.now() + ka)) } /// Keep-alive expire time pub fn keep_alive_expire(&self) -> Option { - if let Some(ka) = self.0.keep_alive { - Some(self.0.date_service.now() + ka) - } else { - None - } + self.keep_alive().map(|ka| self.now() + ka) } #[inline] diff --git a/actix-http/src/header/shared/entity.rs b/actix-http/src/header/shared/entity.rs index eb383cd6f..2505216f2 100644 --- a/actix-http/src/header/shared/entity.rs +++ b/actix-http/src/header/shared/entity.rs @@ -127,9 +127,8 @@ impl Display for EntityTag { impl FromStr for EntityTag { type Err = crate::error::ParseError; - fn from_str(s: &str) -> Result { - let length: usize = s.len(); - let slice = &s[..]; + fn from_str(slice: &str) -> Result { + let length = slice.len(); // Early exits if it doesn't terminate in a DQUOTE. if !slice.ends_with('"') || slice.len() < 2 { return Err(crate::error::ParseError::Header); diff --git a/actix-http/src/header/shared/extended.rs b/actix-http/src/header/shared/extended.rs index 6bdcb7922..9fd4cdfb0 100644 --- a/actix-http/src/header/shared/extended.rs +++ b/actix-http/src/header/shared/extended.rs @@ -88,9 +88,9 @@ pub fn parse_extended_value( }; Ok(ExtendedValue { - value, charset, language_tag, + value, }) } diff --git a/src/config.rs b/src/config.rs index bd9a25c6f..cd14eb4cc 100644 --- a/src/config.rs +++ b/src/config.rs @@ -113,7 +113,7 @@ pub struct AppConfig { impl AppConfig { pub(crate) fn new(secure: bool, addr: SocketAddr, host: String) -> Self { - AppConfig { secure, addr, host } + AppConfig { secure, host, addr } } /// Server host name. diff --git a/src/lib.rs b/src/lib.rs index 16b2ab186..136c462b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -173,11 +173,7 @@ pub mod dev { impl BodyEncoding for ResponseBuilder { fn get_encoding(&self) -> Option { - if let Some(ref enc) = self.extensions().get::() { - Some(enc.0) - } else { - None - } + self.extensions().get::().map(|enc| enc.0) } fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self { @@ -188,11 +184,7 @@ pub mod dev { impl BodyEncoding for Response { fn get_encoding(&self) -> Option { - if let Some(ref enc) = self.extensions().get::() { - Some(enc.0) - } else { - None - } + self.extensions().get::().map(|enc| enc.0) } fn encoding(&mut self, encoding: ContentEncoding) -> &mut Self { diff --git a/src/middleware/compress.rs b/src/middleware/compress.rs index 698ba768e..fc1a85d30 100644 --- a/src/middleware/compress.rs +++ b/src/middleware/compress.rs @@ -197,22 +197,23 @@ impl AcceptEncoding { /// Parse a raw Accept-Encoding header value into an ordered list. pub fn parse(raw: &str, encoding: ContentEncoding) -> ContentEncoding { - let mut encodings: Vec<_> = raw + let mut encodings = raw .replace(' ', "") .split(',') .map(|l| AcceptEncoding::new(l)) - .collect(); + .flatten() + .collect::>(); + encodings.sort(); for enc in encodings { - if let Some(enc) = enc { - if encoding == ContentEncoding::Auto { - return enc.encoding; - } else if encoding == enc.encoding { - return encoding; - } + if encoding == ContentEncoding::Auto { + return enc.encoding; + } else if encoding == enc.encoding { + return encoding; } } + ContentEncoding::Identity } } diff --git a/src/resource.rs b/src/resource.rs index 1a5619de6..d35131cbb 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -449,9 +449,9 @@ impl ServiceFactory for ResourceFactory { .collect::, _>>()?; Ok(ResourceService { + routes, app_data, default, - routes, }) }) } diff --git a/src/test.rs b/src/test.rs index dd2426fec..2ebd64558 100644 --- a/src/test.rs +++ b/src/test.rs @@ -774,10 +774,10 @@ where }; TestServer { - ssl, addr, client, system, + ssl, server, } } diff --git a/tests/test_httpserver.rs b/tests/test_httpserver.rs index aa2b2ca74..12225b7e5 100644 --- a/tests/test_httpserver.rs +++ b/tests/test_httpserver.rs @@ -1,15 +1,11 @@ -use std::sync::mpsc; -use std::{thread, time::Duration}; - #[cfg(feature = "openssl")] extern crate tls_openssl as openssl; -#[cfg(feature = "rustls")] -extern crate tls_rustls as rustls; -#[cfg(feature = "openssl")] -use openssl::ssl::SslAcceptorBuilder; - -use actix_web::{test, web, App, HttpResponse, HttpServer}; +#[cfg(any(unix, feature = "openssl"))] +use { + actix_web::{test, web, App, HttpResponse, HttpServer}, + std::{sync::mpsc, thread, time::Duration}, +}; #[cfg(unix)] #[actix_rt::test] @@ -72,7 +68,7 @@ async fn test_start() { } #[cfg(feature = "openssl")] -fn ssl_acceptor() -> SslAcceptorBuilder { +fn ssl_acceptor() -> openssl::ssl::SslAcceptorBuilder { use openssl::{ pkey::PKey, ssl::{SslAcceptor, SslMethod}, From 9488757c29bf575a113041886050bafad2129a2d Mon Sep 17 00:00:00 2001 From: Thomas de Zeeuw Date: Fri, 19 Mar 2021 12:17:06 +0000 Subject: [PATCH 29/36] Update to socket2 v0.4 (#2092) --- Cargo.toml | 2 +- actix-http-test/Cargo.toml | 2 +- actix-http-test/src/lib.rs | 4 ++-- src/server.rs | 9 +++------ src/test.rs | 4 ++-- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0877edf9c..f3a6271ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,7 +105,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" serde_urlencoded = "0.7" smallvec = "1.6" -socket2 = "0.3.16" +socket2 = "0.4.0" time = { version = "0.2.23", default-features = false, features = ["std"] } tls-openssl = { package = "openssl", version = "0.10.9", optional = true } tls-rustls = { package = "rustls", version = "0.19.0", optional = true } diff --git a/actix-http-test/Cargo.toml b/actix-http-test/Cargo.toml index c1e61556b..a7efc5310 100644 --- a/actix-http-test/Cargo.toml +++ b/actix-http-test/Cargo.toml @@ -42,7 +42,7 @@ bytes = "1" futures-core = { version = "0.3.7", default-features = false } http = "0.2.2" log = "0.4" -socket2 = "0.3" +socket2 = "0.4" serde = "1.0" serde_json = "1.0" slab = "0.4" diff --git a/actix-http-test/src/lib.rs b/actix-http-test/src/lib.rs index 8de07c8d3..138f14313 100644 --- a/actix-http-test/src/lib.rs +++ b/actix-http-test/src/lib.rs @@ -118,10 +118,10 @@ pub async fn test_server_with_addr>( /// Get first available unused address pub fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); - let socket = Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap(); + let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap(); socket.bind(&addr.into()).unwrap(); socket.set_reuse_address(true).unwrap(); - let tcp = socket.into_tcp_listener(); + let tcp = net::TcpListener::from(socket); tcp.local_addr().unwrap() } diff --git a/src/server.rs b/src/server.rs index 99355593a..cfd8ae4b4 100644 --- a/src/server.rs +++ b/src/server.rs @@ -607,17 +607,14 @@ where fn create_tcp_listener(addr: net::SocketAddr, backlog: u32) -> io::Result { use socket2::{Domain, Protocol, Socket, Type}; - let domain = match addr { - net::SocketAddr::V4(_) => Domain::ipv4(), - net::SocketAddr::V6(_) => Domain::ipv6(), - }; - let socket = Socket::new(domain, Type::stream(), Some(Protocol::tcp()))?; + let domain = Domain::for_address(addr); + let socket = Socket::new(domain, Type::STREAM, Some(Protocol::TCP))?; socket.set_reuse_address(true)?; socket.bind(&addr.into())?; // clamp backlog to max u32 that fits in i32 range let backlog = cmp::min(backlog, i32::MAX as u32) as i32; socket.listen(backlog)?; - Ok(socket.into_tcp_listener()) + Ok(net::TcpListener::from(socket)) } #[cfg(feature = "openssl")] diff --git a/src/test.rs b/src/test.rs index 2ebd64558..bc19296e2 100644 --- a/src/test.rs +++ b/src/test.rs @@ -862,10 +862,10 @@ impl TestServerConfig { /// Get first available unused address pub fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); - let socket = Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap(); + let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap(); socket.bind(&addr.into()).unwrap(); socket.set_reuse_address(true).unwrap(); - let tcp = socket.into_tcp_listener(); + let tcp = net::TcpListener::from(socket); tcp.local_addr().unwrap() } From 8d9de768264e78cf62087be44872c17e24f5428a Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Fri, 19 Mar 2021 12:30:53 -0400 Subject: [PATCH 30/36] Simplify handler factory macro (#2086) --- src/handler.rs | 50 +++++++++++++++++++------------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/src/handler.rs b/src/handler.rs index 0016b741e..7e3c5f47e 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -28,17 +28,6 @@ where fn call(&self, param: T) -> R; } -impl Handler<(), R> for F -where - F: Fn() -> R + Clone + 'static, - R: Future, - R::Output: Responder, -{ - fn call(&self, _: ()) -> R { - (self)() - } -} - #[doc(hidden)] /// Extract arguments from request, run factory function and make response. pub struct HandlerService @@ -177,30 +166,29 @@ where } /// FromRequest trait impl for tuples -macro_rules! factory_tuple ({ $(($n:tt, $T:ident)),+} => { - impl Handler<($($T,)+), Res> for Func - where Func: Fn($($T,)+) -> Res + Clone + 'static, +macro_rules! factory_tuple ({ $($param:ident)* } => { + impl Handler<($($param,)*), Res> for Func + where Func: Fn($($param),*) -> Res + Clone + 'static, Res: Future, Res::Output: Responder, { - fn call(&self, param: ($($T,)+)) -> Res { - (self)($(param.$n,)+) + #[allow(non_snake_case)] + fn call(&self, ($($param,)*): ($($param,)*)) -> Res { + (self)($($param,)*) } } }); -#[rustfmt::skip] -mod m { - use super::*; - - factory_tuple!((0, A)); - factory_tuple!((0, A), (1, B)); - factory_tuple!((0, A), (1, B), (2, C)); - factory_tuple!((0, A), (1, B), (2, C), (3, D)); - factory_tuple!((0, A), (1, B), (2, C), (3, D), (4, E)); - factory_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F)); - factory_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G)); - factory_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H)); - factory_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I)); - factory_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J)); -} +factory_tuple! {} +factory_tuple! { A } +factory_tuple! { A B } +factory_tuple! { A B C } +factory_tuple! { A B C D } +factory_tuple! { A B C D E } +factory_tuple! { A B C D E F } +factory_tuple! { A B C D E F G } +factory_tuple! { A B C D E F G H } +factory_tuple! { A B C D E F G H I } +factory_tuple! { A B C D E F G H I J } +factory_tuple! { A B C D E F G H I J K } +factory_tuple! { A B C D E F G H I J K L } From 746d983849d7e5bb5ca4ac242276f6e65cca5643 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Fri, 19 Mar 2021 22:18:06 -0700 Subject: [PATCH 31/36] handle header error with CustomResponder (#2093) --- CHANGES.md | 5 +++++ src/responder.rs | 31 +++++++++++++++---------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ef9ee900a..5df0b6d6d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,11 +4,16 @@ ### Fixed * 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] + ### Removed * The `client` mod was removed. Clients should now use `awc` directly. [871ca5e4](https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7) [#2067]: https://github.com/actix/actix-web/pull/2067 +[#2093]: https://github.com/actix/actix-web/pull/2093 ## 4.0.0-beta.4 - 2021-03-09 diff --git a/src/responder.rs b/src/responder.rs index 92945cdaa..0b6f47c6b 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -155,8 +155,7 @@ impl Responder for BytesMut { pub struct CustomResponder { responder: T, status: Option, - headers: Option, - error: Option, + headers: Result, } impl CustomResponder { @@ -164,8 +163,7 @@ impl CustomResponder { CustomResponder { responder, status: None, - headers: None, - error: None, + headers: Ok(HeaderMap::new()), } } @@ -206,32 +204,33 @@ impl CustomResponder { where H: IntoHeaderPair, { - if self.headers.is_none() { - self.headers = Some(HeaderMap::new()); + 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()), + }; } - match header.try_into_header_pair() { - Ok((key, value)) => self.headers.as_mut().unwrap().append(key, value), - Err(e) => self.error = Some(e.into()), - }; - self } } impl Responder for CustomResponder { fn respond_to(self, req: &HttpRequest) -> HttpResponse { + let headers = match self.headers { + Ok(headers) => headers, + Err(err) => return HttpResponse::from_error(Error::from(err)), + }; + let mut res = self.responder.respond_to(req); if let Some(status) = self.status { *res.status_mut() = status; } - if let Some(ref headers) = self.headers { - for (k, v) in headers { - // TODO: before v4, decide if this should be append instead - res.headers_mut().insert(k.clone(), v.clone()); - } + for (k, v) in headers { + // TODO: before v4, decide if this should be append instead + res.headers_mut().insert(k, v); } res From 1be54efbebe0695e7ad104d2747a1f3f66fb5dea Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Tue, 23 Mar 2021 09:42:46 -0400 Subject: [PATCH 32/36] Simplify service factory macro (#2108) --- src/service.rs | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/service.rs b/src/service.rs index fcbe61a02..491810286 100644 --- a/src/service.rs +++ b/src/service.rs @@ -573,31 +573,28 @@ macro_rules! services { } /// HttpServiceFactory trait impl for tuples -macro_rules! service_tuple ({ $(($n:tt, $T:ident)),+} => { +macro_rules! service_tuple ({ $($T:ident)+ } => { impl<$($T: HttpServiceFactory),+> HttpServiceFactory for ($($T,)+) { + #[allow(non_snake_case)] fn register(self, config: &mut AppService) { - $(self.$n.register(config);)+ + let ($($T,)*) = self; + $($T.register(config);)+ } } }); -#[rustfmt::skip] -mod m { - use super::*; - - service_tuple!((0, A)); - service_tuple!((0, A), (1, B)); - service_tuple!((0, A), (1, B), (2, C)); - service_tuple!((0, A), (1, B), (2, C), (3, D)); - service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E)); - service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F)); - service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G)); - service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H)); - service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I)); - service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J)); - service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K)); - service_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G), (7, H), (8, I), (9, J), (10, K), (11, L)); -} +service_tuple! { A } +service_tuple! { A B } +service_tuple! { A B C } +service_tuple! { A B C D } +service_tuple! { A B C D E } +service_tuple! { A B C D E F } +service_tuple! { A B C D E F G } +service_tuple! { A B C D E F G H } +service_tuple! { A B C D E F G H I } +service_tuple! { A B C D E F G H I J } +service_tuple! { A B C D E F G H I J K } +service_tuple! { A B C D E F G H I J K L } #[cfg(test)] mod tests { From 9704beddf86930251f97557ee5c058c95ec91e71 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Wed, 24 Mar 2021 04:44:03 -0700 Subject: [PATCH 33/36] Relax MessageBody limit to 2048kb (#2110) * relax MessageBody limit to 2048kb * fix clippy * Update awc/src/response.rs Co-authored-by: Rob Ede * fix default body limit Co-authored-by: Rob Ede --- awc/src/response.rs | 83 +++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 45 deletions(-) diff --git a/awc/src/response.rs b/awc/src/response.rs index 40de3dc17..994ddb761 100644 --- a/awc/src/response.rs +++ b/awc/src/response.rs @@ -228,12 +228,13 @@ impl fmt::Debug for ClientResponse { } } +const DEFAULT_BODY_LIMIT: usize = 2 * 1024 * 1024; + /// Future that resolves to a complete HTTP message body. pub struct MessageBody { length: Option, - err: Option, timeout: ResponseTimeout, - fut: Option>, + body: Result, Option>, } impl MessageBody @@ -242,41 +243,38 @@ where { /// Create `MessageBody` for request. pub fn new(res: &mut ClientResponse) -> MessageBody { - 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) - } else { - return Self::err(PayloadError::UnknownLength); + 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, } - } else { - return Self::err(PayloadError::UnknownLength); } - } + None => None, + }; MessageBody { - length: len, - err: None, + length, timeout: std::mem::take(&mut res.timeout), - fut: Some(ReadBody::new(res.take_payload(), 262_144)), + body: Ok(ReadBody::new(res.take_payload(), DEFAULT_BODY_LIMIT)), } } - /// Change max size of payload. By default max size is 256kB + /// Change max size of payload. By default max size is 2048kB pub fn limit(mut self, limit: usize) -> Self { - if let Some(ref mut fut) = self.fut { - fut.limit = limit; + if let Ok(ref mut body) = self.body { + body.limit = limit; } self } fn err(e: PayloadError) -> Self { MessageBody { - fut: None, - err: Some(e), length: None, timeout: ResponseTimeout::default(), + body: Err(Some(e)), } } } @@ -290,19 +288,20 @@ where fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); - if let Some(err) = this.err.take() { - return Poll::Ready(Err(err)); - } + 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)); + } + } - if let Some(len) = this.length.take() { - if len > this.fut.as_ref().unwrap().limit { - return Poll::Ready(Err(PayloadError::Overflow)); + this.timeout.poll_timeout(cx)?; + + Pin::new(body).poll(cx) } } - - this.timeout.poll_timeout(cx)?; - - Pin::new(&mut this.fut.as_mut().unwrap()).poll(cx) } } @@ -415,7 +414,7 @@ impl ReadBody { fn new(stream: Payload, limit: usize) -> Self { Self { stream, - buf: BytesMut::with_capacity(std::cmp::min(limit, 32768)), + buf: BytesMut::new(), limit, } } @@ -430,20 +429,14 @@ where fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.get_mut(); - loop { - return match Pin::new(&mut this.stream).poll_next(cx)? { - Poll::Ready(Some(chunk)) => { - if (this.buf.len() + chunk.len()) > this.limit { - Poll::Ready(Err(PayloadError::Overflow)) - } else { - this.buf.extend_from_slice(&chunk); - continue; - } - } - Poll::Ready(None) => Poll::Ready(Ok(this.buf.split().freeze())), - Poll::Pending => Poll::Pending, - }; + 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())) } } @@ -462,7 +455,7 @@ mod tests { _ => unreachable!("error"), } - let mut req = TestResponse::with_header(header::CONTENT_LENGTH, "1000000").finish(); + let mut req = TestResponse::with_header(header::CONTENT_LENGTH, "10000000").finish(); match req.body().await.err().unwrap() { PayloadError::Overflow => {} _ => unreachable!("error"), From 3188ef573159263e281f25416b4f960ee6d67cad Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Thu, 25 Mar 2021 08:45:52 +0000 Subject: [PATCH 34/36] don't use rust annotation on code doc blocks --- actix-files/src/files.rs | 2 +- actix-files/src/lib.rs | 2 +- actix-files/src/named.rs | 6 ++--- actix-http-test/src/lib.rs | 2 +- actix-http/src/client/connector.rs | 2 +- actix-http/src/error.rs | 2 +- actix-http/src/header/common/cache_control.rs | 2 +- actix-http/src/response.rs | 8 +++---- actix-http/src/test.rs | 2 +- actix-multipart/src/extractor.rs | 2 +- actix-web-codegen/src/lib.rs | 6 ++--- awc/src/lib.rs | 2 +- awc/src/request.rs | 6 ++--- benches/service.rs | 2 +- src/app.rs | 16 ++++++------- src/data.rs | 2 +- src/extract.rs | 4 ++-- src/guard.rs | 10 ++++---- src/lib.rs | 2 +- src/middleware/compat.rs | 2 +- src/middleware/compress.rs | 2 +- src/middleware/condition.rs | 2 +- src/middleware/default_headers.rs | 2 +- src/middleware/err_handlers.rs | 2 +- src/middleware/logger.rs | 4 ++-- src/middleware/normalize.rs | 2 +- src/request.rs | 6 ++--- src/request_data.rs | 2 +- src/resource.rs | 16 ++++++------- src/responder.rs | 8 +++---- src/route.rs | 8 +++---- src/scope.rs | 14 +++++------ src/server.rs | 4 ++-- src/service.rs | 2 +- src/test.rs | 18 +++++++------- src/web.rs | 24 +++++++++---------- 36 files changed, 99 insertions(+), 99 deletions(-) diff --git a/actix-files/src/files.rs b/actix-files/src/files.rs index 6f8b28bbf..292e3fdf3 100644 --- a/actix-files/src/files.rs +++ b/actix-files/src/files.rs @@ -19,7 +19,7 @@ use crate::{ /// /// `Files` service must be registered with `App::service()` method. /// -/// ```rust +/// ``` /// use actix_web::App; /// use actix_files::Files; /// diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 3c34c0403..018079b21 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -3,7 +3,7 @@ //! Provides a non-blocking service for serving static files from disk. //! //! # Example -//! ```rust +//! ``` //! use actix_web::App; //! use actix_files::Files; //! diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index a688b2e6c..2846646a2 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -60,7 +60,7 @@ impl NamedFile { /// /// # Examples /// - /// ```rust + /// ``` /// use actix_files::NamedFile; /// use std::io::{self, Write}; /// use std::env; @@ -137,7 +137,7 @@ impl NamedFile { /// /// # Examples /// - /// ```rust + /// ``` /// use actix_files::NamedFile; /// /// let file = NamedFile::open("foo.txt"); @@ -156,7 +156,7 @@ impl NamedFile { /// /// # Examples /// - /// ```rust + /// ``` /// # use std::io; /// use actix_files::NamedFile; /// diff --git a/actix-http-test/src/lib.rs b/actix-http-test/src/lib.rs index 138f14313..3749b78ca 100644 --- a/actix-http-test/src/lib.rs +++ b/actix-http-test/src/lib.rs @@ -26,7 +26,7 @@ use socket2::{Domain, Protocol, Socket, Type}; /// /// # Examples /// -/// ```rust +/// ``` /// use actix_http::HttpService; /// use actix_http_test::TestServer; /// use actix_web::{web, App, HttpResponse, Error}; diff --git a/actix-http/src/client/connector.rs b/actix-http/src/client/connector.rs index 1a5f32880..6996677d2 100644 --- a/actix-http/src/client/connector.rs +++ b/actix-http/src/client/connector.rs @@ -47,7 +47,7 @@ enum SslConnector { /// The `Connector` type uses a builder-like combinator pattern for service /// construction that finishes by calling the `.finish()` method. /// -/// ```rust,ignore +/// ```ignore /// use std::time::Duration; /// use actix_http::client::Connector; /// diff --git a/actix-http/src/error.rs b/actix-http/src/error.rs index d9b97d6a6..1354e998e 100644 --- a/actix-http/src/error.rs +++ b/actix-http/src/error.rs @@ -483,7 +483,7 @@ where /// response as opposite to *INTERNAL SERVER ERROR* which is defined by /// default. /// -/// ```rust +/// ``` /// # use std::io; /// # use actix_http::*; /// diff --git a/actix-http/src/header/common/cache_control.rs b/actix-http/src/header/common/cache_control.rs index 94ce9a750..b19823d22 100644 --- a/actix-http/src/header/common/cache_control.rs +++ b/actix-http/src/header/common/cache_control.rs @@ -36,7 +36,7 @@ use crate::header::{ /// builder.insert_header(CacheControl(vec![CacheDirective::MaxAge(86400u32)])); /// ``` /// -/// ```rust +/// ``` /// use actix_http::Response; /// use actix_http::http::header::{CacheControl, CacheDirective}; /// diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index d581fd293..94f12bcc3 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -357,7 +357,7 @@ impl ResponseBuilder { /// Insert a header, replacing any that were set with an equivalent field name. /// - /// ```rust + /// ``` /// # use actix_http::Response; /// use actix_http::http::header::ContentType; /// @@ -384,7 +384,7 @@ impl ResponseBuilder { /// Append a header, keeping any that were set with an equivalent field name. /// - /// ```rust + /// ``` /// # use actix_http::Response; /// use actix_http::http::header::ContentType; /// @@ -525,7 +525,7 @@ impl ResponseBuilder { /// Set a cookie /// - /// ```rust + /// ``` /// use actix_http::{http, Request, Response}; /// /// fn index(req: Request) -> Response { @@ -555,7 +555,7 @@ impl ResponseBuilder { /// Remove cookie /// - /// ```rust + /// ``` /// use actix_http::{http, Request, Response, HttpMessage}; /// /// fn index(req: Request) -> Response { diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs index 870a656df..ad3dc74b2 100644 --- a/actix-http/src/test.rs +++ b/actix-http/src/test.rs @@ -26,7 +26,7 @@ use crate::{ /// Test `Request` builder /// -/// ```rust,ignore +/// ```ignore /// # use http::{header, StatusCode}; /// # use actix_web::*; /// use actix_web::test::TestRequest; diff --git a/actix-multipart/src/extractor.rs b/actix-multipart/src/extractor.rs index 6aaa415c4..bffbe8a1b 100644 --- a/actix-multipart/src/extractor.rs +++ b/actix-multipart/src/extractor.rs @@ -10,7 +10,7 @@ use crate::server::Multipart; /// /// ## Server example /// -/// ```rust +/// ``` /// use futures_util::stream::{Stream, StreamExt}; /// use actix_web::{web, HttpResponse, Error}; /// use actix_multipart as mp; diff --git a/actix-web-codegen/src/lib.rs b/actix-web-codegen/src/lib.rs index 48414d491..336345014 100644 --- a/actix-web-codegen/src/lib.rs +++ b/actix-web-codegen/src/lib.rs @@ -82,7 +82,7 @@ mod route; /// /// # Example /// -/// ```rust +/// ``` /// # use actix_web::HttpResponse; /// # use actix_web_codegen::route; /// #[route("/test", method="GET", method="HEAD")] @@ -127,7 +127,7 @@ code, e.g `my_guard` or `my_module::my_guard`. # Example -```rust +``` # use actix_web::HttpResponse; # use actix_web_codegen::"#, stringify!($method), "; #[", stringify!($method), r#"("/")] @@ -162,7 +162,7 @@ method_macro! { /// This macro can be applied with `#[actix_web::main]` when used in Actix Web applications. /// /// # Examples -/// ```rust +/// ``` /// #[actix_web_codegen::main] /// async fn main() { /// async { println!("Hello world"); }.await diff --git a/awc/src/lib.rs b/awc/src/lib.rs index 8addf4d8b..c7bb68a8f 100644 --- a/awc/src/lib.rs +++ b/awc/src/lib.rs @@ -131,7 +131,7 @@ pub use self::sender::SendClientRequest; /// /// ## Examples /// -/// ```rust +/// ``` /// use awc::Client; /// /// #[actix_rt::main] diff --git a/awc/src/request.rs b/awc/src/request.rs index 3e9babb7d..a847b09a3 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -36,7 +36,7 @@ cfg_if::cfg_if! { /// This type can be used to construct an instance of `ClientRequest` through a /// builder-like pattern. /// -/// ```rust +/// ``` /// #[actix_rt::main] /// async fn main() { /// let response = awc::Client::new() @@ -190,7 +190,7 @@ impl ClientRequest { /// Append a header, keeping any that were set with an equivalent field name. /// - /// ```rust + /// ``` /// # #[actix_rt::main] /// # async fn main() { /// # use awc::Client; @@ -271,7 +271,7 @@ impl ClientRequest { /// Set a cookie /// - /// ```rust + /// ``` /// #[actix_rt::main] /// async fn main() { /// let resp = awc::Client::new().get("https://www.rust-lang.org") diff --git a/benches/service.rs b/benches/service.rs index 0d3264857..30708477d 100644 --- a/benches/service.rs +++ b/benches/service.rs @@ -9,7 +9,7 @@ use actix_web::test::{init_service, ok_service, TestRequest}; /// Criterion Benchmark for async Service /// Should be used from within criterion group: -/// ```rust,ignore +/// ```ignore /// let mut criterion: ::criterion::Criterion<_> = /// ::criterion::Criterion::default().configure_from_args(); /// bench_async_service(&mut criterion, ok_service(), "async_service_direct"); diff --git a/src/app.rs b/src/app.rs index 7a26a3a89..f2c6bce8a 100644 --- a/src/app.rs +++ b/src/app.rs @@ -79,7 +79,7 @@ where /// uses `Arc` so data could be created outside of app factory and clones could /// be stored via `App::app_data()` method. /// - /// ```rust + /// ``` /// use std::cell::Cell; /// use actix_web::{web, App, HttpResponse, Responder}; /// @@ -152,7 +152,7 @@ where /// different module or even library. For example, /// some of the resource's configuration could be moved to different module. /// - /// ```rust + /// ``` /// use actix_web::{web, App, HttpResponse}; /// /// // this function could be located in different module @@ -185,7 +185,7 @@ where /// This method can be used multiple times with same path, in that case /// multiple resources with one route would be registered for same resource path. /// - /// ```rust + /// ``` /// use actix_web::{web, App, HttpResponse}; /// /// async fn index(data: web::Path<(String, String)>) -> &'static str { @@ -228,7 +228,7 @@ where /// /// It is possible to use services like `Resource`, `Route`. /// - /// ```rust + /// ``` /// use actix_web::{web, App, HttpResponse}; /// /// async fn index() -> &'static str { @@ -246,7 +246,7 @@ where /// /// It is also possible to use static files as default service. /// - /// ```rust + /// ``` /// use actix_web::{web, App, HttpResponse}; /// /// fn main() { @@ -283,7 +283,7 @@ where /// and are never considered for matching at request time. Calls to /// `HttpRequest::url_for()` will work as expected. /// - /// ```rust + /// ``` /// use actix_web::{web, App, HttpRequest, HttpResponse, Result}; /// /// async fn index(req: HttpRequest) -> Result { @@ -325,7 +325,7 @@ where /// the builder chain. Consequently, the *first* middleware registered /// in the builder chain is the *last* to execute during request processing. /// - /// ```rust + /// ``` /// use actix_service::Service; /// use actix_web::{middleware, web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; @@ -382,7 +382,7 @@ where /// /// Use middleware when you need to read or modify *every* request or response in some way. /// - /// ```rust + /// ``` /// use actix_service::Service; /// use actix_web::{web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; diff --git a/src/data.rs b/src/data.rs index 0336553ca..56ecdb8ae 100644 --- a/src/data.rs +++ b/src/data.rs @@ -37,7 +37,7 @@ pub(crate) type FnDataFactory = /// If route data is not set for a handler, using `Data` extractor would cause *Internal /// Server Error* response. /// -/// ```rust +/// ``` /// use std::sync::Mutex; /// use actix_web::{web, App, HttpResponse, Responder}; /// diff --git a/src/extract.rs b/src/extract.rs index 921d9fc36..8851481e3 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -51,7 +51,7 @@ pub trait FromRequest: Sized { /// /// ## Example /// -/// ```rust +/// ``` /// use actix_web::{web, dev, App, Error, HttpRequest, FromRequest}; /// use actix_web::error::ErrorBadRequest; /// use futures_util::future::{ok, err, Ready}; @@ -143,7 +143,7 @@ where /// /// ## Example /// -/// ```rust +/// ``` /// use actix_web::{web, dev, App, Result, Error, HttpRequest, FromRequest}; /// use actix_web::error::ErrorBadRequest; /// use futures_util::future::{ok, err, Ready}; diff --git a/src/guard.rs b/src/guard.rs index 5d0de58c2..3a1f5bb14 100644 --- a/src/guard.rs +++ b/src/guard.rs @@ -12,7 +12,7 @@ //! to store extra attributes on a request by using the `Extensions` container. //! Extensions containers are available via the `RequestHead::extensions()` method. //! -//! ```rust +//! ``` //! use actix_web::{web, http, dev, guard, App, HttpResponse}; //! //! fn main() { @@ -42,7 +42,7 @@ pub trait Guard { /// Create guard object for supplied function. /// -/// ```rust +/// ``` /// use actix_web::{guard, web, App, HttpResponse}; /// /// fn main() { @@ -85,7 +85,7 @@ where /// Return guard that matches if any of supplied guards. /// -/// ```rust +/// ``` /// use actix_web::{web, guard, App, HttpResponse}; /// /// fn main() { @@ -124,7 +124,7 @@ impl Guard for AnyGuard { /// Return guard that matches if all of the supplied guards. /// -/// ```rust +/// ``` /// use actix_web::{guard, web, App, HttpResponse}; /// /// fn main() { @@ -259,7 +259,7 @@ impl Guard for HeaderGuard { /// Return predicate that matches if request contains specified Host name. /// -/// ```rust +/// ``` /// use actix_web::{web, guard::Host, App, HttpResponse}; /// /// fn main() { diff --git a/src/lib.rs b/src/lib.rs index 136c462b8..7a6498546 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! //! ## Example //! -//! ```rust,no_run +//! ```no_run //! use actix_web::{get, web, App, HttpServer, Responder}; //! //! #[get("/{id}/{name}/index.html")] diff --git a/src/middleware/compat.rs b/src/middleware/compat.rs index 6f60264b1..71193a5c5 100644 --- a/src/middleware/compat.rs +++ b/src/middleware/compat.rs @@ -16,7 +16,7 @@ use crate::{error::Error, service::ServiceResponse}; /// [`Scope::wrap`](crate::Scope::wrap) and [`Condition`](super::Condition). /// /// # Examples -/// ```rust +/// ``` /// use actix_web::middleware::{Logger, Compat}; /// use actix_web::{App, web}; /// diff --git a/src/middleware/compress.rs b/src/middleware/compress.rs index fc1a85d30..a397bccd6 100644 --- a/src/middleware/compress.rs +++ b/src/middleware/compress.rs @@ -31,7 +31,7 @@ use crate::{ /// encoding to `ContentEncoding::Identity`. /// /// # Examples -/// ```rust +/// ``` /// use actix_web::{web, middleware, App, HttpResponse}; /// /// let app = App::new() diff --git a/src/middleware/condition.rs b/src/middleware/condition.rs index 63a90c853..dd599a0cb 100644 --- a/src/middleware/condition.rs +++ b/src/middleware/condition.rs @@ -12,7 +12,7 @@ use futures_util::future::{Either, FutureExt, LocalBoxFuture}; /// middleware for a workaround. /// /// # Examples -/// ```rust +/// ``` /// use actix_web::middleware::{Condition, NormalizePath}; /// use actix_web::App; /// diff --git a/src/middleware/default_headers.rs b/src/middleware/default_headers.rs index a36cc2f29..12d70ab2c 100644 --- a/src/middleware/default_headers.rs +++ b/src/middleware/default_headers.rs @@ -29,7 +29,7 @@ use crate::{ /// Headers with the same key that are already set in a response will *not* be overwritten. /// /// # Examples -/// ```rust +/// ``` /// use actix_web::{web, http, middleware, App, HttpResponse}; /// /// fn main() { diff --git a/src/middleware/err_handlers.rs b/src/middleware/err_handlers.rs index 06e88cefd..fddd87a99 100644 --- a/src/middleware/err_handlers.rs +++ b/src/middleware/err_handlers.rs @@ -34,7 +34,7 @@ type ErrorHandler = dyn Fn(ServiceResponse) -> Result) -> String { "jwt_uid".to_owned() } /// Logger::new("example %{JWT_ID}xi") diff --git a/src/middleware/normalize.rs b/src/middleware/normalize.rs index ea21a7215..2a97a047b 100644 --- a/src/middleware/normalize.rs +++ b/src/middleware/normalize.rs @@ -54,7 +54,7 @@ impl Default for TrailingSlash { /// `TrailingSlash::Always` behavior), as shown in the example tests below. /// /// # Examples -/// ```rust +/// ``` /// use actix_web::{web, middleware, App}; /// /// # actix_web::rt::System::new().block_on(async { diff --git a/src/request.rs b/src/request.rs index 514b7466e..15c97345c 100644 --- a/src/request.rs +++ b/src/request.rs @@ -159,7 +159,7 @@ impl HttpRequest { /// Generate url for named resource /// - /// ```rust + /// ``` /// # use actix_web::{web, App, HttpRequest, HttpResponse}; /// # /// fn index(req: HttpRequest) -> HttpResponse { @@ -231,7 +231,7 @@ impl HttpRequest { /// /// If `App::data` was used to store object, use `Data`: /// - /// ```rust,ignore + /// ```ignore /// let opt_t = req.app_data::>(); /// ``` pub fn app_data(&self) -> Option<&T> { @@ -302,7 +302,7 @@ impl Drop for HttpRequest { /// /// ## Example /// -/// ```rust +/// ``` /// use actix_web::{web, App, HttpRequest}; /// use serde_derive::Deserialize; /// diff --git a/src/request_data.rs b/src/request_data.rs index beee8ac12..fc711d011 100644 --- a/src/request_data.rs +++ b/src/request_data.rs @@ -23,7 +23,7 @@ use crate::{dev::Payload, FromRequest, HttpRequest}; /// provided to make this potential foot-gun more obvious. /// /// # Example -/// ```rust,no_run +/// ```no_run /// # use actix_web::{web, HttpResponse, HttpRequest, Responder}; /// /// #[derive(Debug, Clone, PartialEq)] diff --git a/src/resource.rs b/src/resource.rs index d35131cbb..8f356c76d 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -35,7 +35,7 @@ type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Err /// and check guards for specific route, if request matches all /// guards, route considered matched and route handler get called. /// -/// ```rust +/// ``` /// use actix_web::{web, App, HttpResponse}; /// /// fn main() { @@ -97,7 +97,7 @@ where /// Add match guard to a resource. /// - /// ```rust + /// ``` /// use actix_web::{web, guard, App, HttpResponse}; /// /// async fn index(data: web::Path<(String, String)>) -> &'static str { @@ -130,7 +130,7 @@ where /// Register a new route. /// - /// ```rust + /// ``` /// use actix_web::{web, guard, App, HttpResponse}; /// /// fn main() { @@ -147,7 +147,7 @@ where /// Multiple routes could be added to a resource. Resource object uses /// match guards for route selection. /// - /// ```rust + /// ``` /// use actix_web::{web, guard, App}; /// /// fn main() { @@ -172,7 +172,7 @@ where /// Provided data is available for all routes registered for the current resource. /// Resource data overrides data registered by `App::data()` method. /// - /// ```rust + /// ``` /// use actix_web::{web, App, FromRequest}; /// /// /// extract text data from request @@ -211,7 +211,7 @@ where /// Register a new route and add handler. This route matches all requests. /// - /// ```rust + /// ``` /// use actix_web::*; /// /// fn index(req: HttpRequest) -> HttpResponse { @@ -223,7 +223,7 @@ where /// /// This is shortcut for: /// - /// ```rust + /// ``` /// # extern crate actix_web; /// # use actix_web::*; /// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() } @@ -289,7 +289,7 @@ where /// Resource level middlewares are not allowed to change response /// type (i.e modify response's body). /// - /// ```rust + /// ``` /// use actix_service::Service; /// use actix_web::{web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; diff --git a/src/responder.rs b/src/responder.rs index 0b6f47c6b..b75c95083 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -18,7 +18,7 @@ pub trait Responder { /// Override a status code for a Responder. /// - /// ```rust + /// ``` /// use actix_web::{http::StatusCode, HttpRequest, Responder}; /// /// fn index(req: HttpRequest) -> impl Responder { @@ -36,7 +36,7 @@ pub trait Responder { /// /// Overrides other headers with the same name. /// - /// ```rust + /// ``` /// use actix_web::{web, HttpRequest, Responder}; /// use serde::Serialize; /// @@ -169,7 +169,7 @@ impl CustomResponder { /// Override a status code for the Responder's response. /// - /// ```rust + /// ``` /// use actix_web::{HttpRequest, Responder, http::StatusCode}; /// /// fn index(req: HttpRequest) -> impl Responder { @@ -185,7 +185,7 @@ impl CustomResponder { /// /// Overrides other headers with the same name. /// - /// ```rust + /// ``` /// use actix_web::{web, HttpRequest, Responder}; /// use serde::Serialize; /// diff --git a/src/route.rs b/src/route.rs index c157025b8..0a297b456 100644 --- a/src/route.rs +++ b/src/route.rs @@ -90,7 +90,7 @@ impl Service for RouteService { impl Route { /// Add method guard to the route. /// - /// ```rust + /// ``` /// # use actix_web::*; /// # fn main() { /// App::new().service(web::resource("/path").route( @@ -110,7 +110,7 @@ impl Route { /// Add guard to the route. /// - /// ```rust + /// ``` /// # use actix_web::*; /// # fn main() { /// App::new().service(web::resource("/path").route( @@ -128,7 +128,7 @@ impl Route { /// Set handler function, use request extractors for parameters. /// - /// ```rust + /// ``` /// use actix_web::{web, http, App}; /// use serde_derive::Deserialize; /// @@ -152,7 +152,7 @@ impl Route { /// /// It is possible to use multiple extractors for one handler function. /// - /// ```rust + /// ``` /// # use std::collections::HashMap; /// # use serde_derive::Deserialize; /// use actix_web::{web, App}; diff --git a/src/scope.rs b/src/scope.rs index e96f5b9e8..693d6860f 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -40,7 +40,7 @@ type HttpNewService = BoxServiceFactory<(), ServiceRequest, ServiceResponse, Err /// You can get variable path segments from `HttpRequest::match_info()`. /// `Path` extractor also is able to extract scope level variable segments. /// -/// ```rust +/// ``` /// use actix_web::{web, App, HttpResponse}; /// /// fn main() { @@ -97,7 +97,7 @@ where { /// Add match guard to a scope. /// - /// ```rust + /// ``` /// use actix_web::{web, guard, App, HttpRequest, HttpResponse}; /// /// async fn index(data: web::Path<(String, String)>) -> &'static str { @@ -123,7 +123,7 @@ where /// Set or override application data. Application data could be accessed /// by using `Data` extractor where `T` is data type. /// - /// ```rust + /// ``` /// use std::cell::Cell; /// use actix_web::{web, App, HttpResponse, Responder}; /// @@ -168,7 +168,7 @@ where /// different module or even library. For example, /// some of the resource's configuration could be moved to different module. /// - /// ```rust + /// ``` /// # extern crate actix_web; /// use actix_web::{web, middleware, App, HttpResponse}; /// @@ -215,7 +215,7 @@ where /// * *Scope* is a set of resources with common root path. /// * "StaticFiles" is a service for static files support /// - /// ```rust + /// ``` /// use actix_web::{web, App, HttpRequest}; /// /// struct AppState; @@ -247,7 +247,7 @@ where /// This method can be called multiple times, in that case /// multiple resources with one route would be registered for same resource path. /// - /// ```rust + /// ``` /// use actix_web::{web, App, HttpResponse}; /// /// async fn index(data: web::Path<(String, String)>) -> &'static str { @@ -341,7 +341,7 @@ where /// to Route or Application level middleware, in that Scope-level middleware /// can not modify ServiceResponse. /// - /// ```rust + /// ``` /// use actix_service::Service; /// use actix_web::{web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; diff --git a/src/server.rs b/src/server.rs index cfd8ae4b4..97c4fdaa2 100644 --- a/src/server.rs +++ b/src/server.rs @@ -35,7 +35,7 @@ struct Config { /// /// Create new HTTP server with application factory. /// -/// ```rust,no_run +/// ```no_run /// use actix_web::{web, App, HttpResponse, HttpServer}; /// /// #[actix_rt::main] @@ -588,7 +588,7 @@ where /// This methods panics if no socket address can be bound or an `Actix` system is not yet /// configured. /// - /// ```rust,no_run + /// ```no_run /// use std::io; /// use actix_web::{web, App, HttpResponse, HttpServer}; /// diff --git a/src/service.rs b/src/service.rs index 491810286..32f152f7d 100644 --- a/src/service.rs +++ b/src/service.rs @@ -462,7 +462,7 @@ impl WebService { /// Add match guard to a web service. /// - /// ```rust + /// ``` /// use actix_web::{web, guard, dev, App, Error, HttpResponse}; /// /// async fn index(req: dev::ServiceRequest) -> Result { diff --git a/src/test.rs b/src/test.rs index bc19296e2..7e8bf1069 100644 --- a/src/test.rs +++ b/src/test.rs @@ -54,7 +54,7 @@ pub fn default_service( /// This method accepts application builder instance, and constructs /// service. /// -/// ```rust +/// ``` /// use actix_service::Service; /// use actix_web::{test, web, App, HttpResponse, http::StatusCode}; /// @@ -101,7 +101,7 @@ where /// Calls service and waits for response future completion. /// -/// ```rust +/// ``` /// use actix_web::{test, web, App, HttpResponse, http::StatusCode}; /// /// #[actix_rt::test] @@ -131,7 +131,7 @@ where /// Helper function that returns a response body of a TestRequest /// -/// ```rust +/// ``` /// use actix_web::{test, web, App, HttpResponse, http::header}; /// use bytes::Bytes; /// @@ -174,7 +174,7 @@ where /// Helper function that returns a response body of a ServiceResponse. /// -/// ```rust +/// ``` /// use actix_web::{test, web, App, HttpResponse, http::header}; /// use bytes::Bytes; /// @@ -212,7 +212,7 @@ where /// Helper function that returns a deserialized response body of a ServiceResponse. /// -/// ```rust +/// ``` /// use actix_web::{App, test, web, HttpResponse, http::header}; /// use serde::{Serialize, Deserialize}; /// @@ -271,7 +271,7 @@ where /// Helper function that returns a deserialized response body of a TestRequest /// -/// ```rust +/// ``` /// use actix_web::{App, test, web, HttpResponse, http::header}; /// use serde::{Serialize, Deserialize}; /// @@ -324,7 +324,7 @@ where /// * `TestRequest::to_srv_response` creates `ServiceResponse` instance. /// * `TestRequest::to_http_request` creates `HttpRequest` instance, which is used for testing handlers. /// -/// ```rust +/// ``` /// use actix_web::{test, HttpRequest, HttpResponse, HttpMessage}; /// use actix_web::http::{header, StatusCode}; /// @@ -572,7 +572,7 @@ impl TestRequest { /// /// # Examples /// -/// ```rust +/// ``` /// use actix_web::{web, test, App, HttpResponse, Error}; /// /// async fn my_handler() -> Result { @@ -612,7 +612,7 @@ where /// /// # Examples /// -/// ```rust +/// ``` /// use actix_web::{web, test, App, HttpResponse, Error}; /// /// async fn my_handler() -> Result { diff --git a/src/web.rs b/src/web.rs index 1cef37109..8662848a4 100644 --- a/src/web.rs +++ b/src/web.rs @@ -42,7 +42,7 @@ pub use crate::types::*; /// `/users/{userid}/{friend}` and store `userid` and `friend` in /// the exposed `Params` object: /// -/// ```rust +/// ``` /// # extern crate actix_web; /// use actix_web::{web, App, HttpResponse}; /// @@ -61,7 +61,7 @@ pub fn resource(path: T) -> Resource { /// Scopes collect multiple paths under a common path prefix. /// Scope path can contain variable path segments as resources. /// -/// ```rust +/// ``` /// use actix_web::{web, App, HttpResponse}; /// /// let app = App::new().service( @@ -88,7 +88,7 @@ pub fn route() -> Route { /// Create *route* with `GET` method guard. /// -/// ```rust +/// ``` /// use actix_web::{web, App, HttpResponse}; /// /// let app = App::new().service( @@ -106,7 +106,7 @@ pub fn get() -> Route { /// Create *route* with `POST` method guard. /// -/// ```rust +/// ``` /// use actix_web::{web, App, HttpResponse}; /// /// let app = App::new().service( @@ -124,7 +124,7 @@ pub fn post() -> Route { /// Create *route* with `PUT` method guard. /// -/// ```rust +/// ``` /// use actix_web::{web, App, HttpResponse}; /// /// let app = App::new().service( @@ -142,7 +142,7 @@ pub fn put() -> Route { /// Create *route* with `PATCH` method guard. /// -/// ```rust +/// ``` /// use actix_web::{web, App, HttpResponse}; /// /// let app = App::new().service( @@ -160,7 +160,7 @@ pub fn patch() -> Route { /// Create *route* with `DELETE` method guard. /// -/// ```rust +/// ``` /// use actix_web::{web, App, HttpResponse}; /// /// let app = App::new().service( @@ -178,7 +178,7 @@ pub fn delete() -> Route { /// Create *route* with `HEAD` method guard. /// -/// ```rust +/// ``` /// use actix_web::{web, App, HttpResponse}; /// /// let app = App::new().service( @@ -196,7 +196,7 @@ pub fn head() -> Route { /// Create *route* with `TRACE` method guard. /// -/// ```rust +/// ``` /// use actix_web::{web, App, HttpResponse}; /// /// let app = App::new().service( @@ -214,7 +214,7 @@ pub fn trace() -> Route { /// Create *route* and add method guard. /// -/// ```rust +/// ``` /// use actix_web::{web, http, App, HttpResponse}; /// /// let app = App::new().service( @@ -232,7 +232,7 @@ pub fn method(method: Method) -> Route { /// Create a new route and add handler. /// -/// ```rust +/// ``` /// use actix_web::{web, App, HttpResponse, Responder}; /// /// async fn index() -> impl Responder { @@ -256,7 +256,7 @@ where /// Create raw service for a specific path. /// -/// ```rust +/// ``` /// use actix_web::{dev, web, guard, App, Error, HttpResponse}; /// /// async fn my_service(req: dev::ServiceRequest) -> Result { From 8c2ce2dedb4b3fd98654ed7451641408ab535b09 Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Thu, 25 Mar 2021 15:47:37 -0700 Subject: [PATCH 35/36] fix awc compress feature (#2116) --- actix-http/Cargo.toml | 1 - awc/CHANGES.md | 4 +++- awc/Cargo.toml | 1 - awc/src/request.rs | 19 +++++++------------ 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index e1aebb76b..c24878404 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -55,7 +55,6 @@ base64 = "0.13" bitflags = "1.2" bytes = "1" bytestring = "1" -cfg-if = "1" cookie = { version = "0.14.1", features = ["percent-encode"], optional = true } derive_more = "0.99.5" encoding_rs = "0.8" diff --git a/awc/CHANGES.md b/awc/CHANGES.md index d2cb7c009..4f72e3f93 100644 --- a/awc/CHANGES.md +++ b/awc/CHANGES.md @@ -2,9 +2,11 @@ ## Unreleased - 2021-xx-xx ### Changed -* `ConnectorService` type is renamed to `BoxConnectorService` [#2081] +* `ConnectorService` type is renamed to `BoxConnectorService`. [#2081] +* Fix http/https encoding when enabling `compress` feature. [#2116] [#2081]: https://github.com/actix/actix-web/pull/2081 +[#2116]: https://github.com/actix/actix-web/pull/2116 ## 3.0.0-beta.3 - 2021-03-08 diff --git a/awc/Cargo.toml b/awc/Cargo.toml index ec2e03a96..b555ebb22 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -51,7 +51,6 @@ actix-rt = { version = "2.1", default-features = false } base64 = "0.13" bytes = "1" -cfg-if = "1.0" derive_more = "0.99.5" futures-core = { version = "0.3.7", default-features = false } itoa = "0.4" diff --git a/awc/src/request.rs b/awc/src/request.rs index a847b09a3..8b896a00d 100644 --- a/awc/src/request.rs +++ b/awc/src/request.rs @@ -21,15 +21,10 @@ use crate::frozen::FrozenClientRequest; use crate::sender::{PrepForSendingError, RequestSender, SendClientRequest}; use crate::ClientConfig; -cfg_if::cfg_if! { - if #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] { - const HTTPS_ENCODING: &str = "br, gzip, deflate"; - } else if #[cfg(feature = "compress")] { - const HTTPS_ENCODING: &str = "br"; - } else { - const HTTPS_ENCODING: &str = "identity"; - } -} +#[cfg(feature = "compress")] +const HTTPS_ENCODING: &str = "br, gzip, deflate"; +#[cfg(not(feature = "compress"))] +const HTTPS_ENCODING: &str = "br"; /// An HTTP Client request builder /// @@ -521,11 +516,11 @@ impl ClientRequest { .unwrap_or(true); if https { - slf = slf.insert_header_if_none((header::ACCEPT_ENCODING, HTTPS_ENCODING)) + slf = slf.insert_header_if_none((header::ACCEPT_ENCODING, HTTPS_ENCODING)); } else { - #[cfg(any(feature = "flate2-zlib", feature = "flate2-rust"))] + #[cfg(feature = "compress")] { - slf = slf.insert_header_if_none((header::ACCEPT_ENCODING, "gzip, deflate")) + slf = slf.insert_header_if_none((header::ACCEPT_ENCODING, "gzip, deflate")); } }; } From 2f7f1fa97aef546a369495a177c6dc893c26f4bf Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Thu, 25 Mar 2021 17:05:31 -0700 Subject: [PATCH 36/36] fix broken pipe for h2 when client is instantly dropped (#2113) --- actix-http/src/client/connection.rs | 56 +++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/actix-http/src/client/connection.rs b/actix-http/src/client/connection.rs index 78101397d..0e3e97f3f 100644 --- a/actix-http/src/client/connection.rs +++ b/actix-http/src/client/connection.rs @@ -174,7 +174,13 @@ impl H2ConnectionInner { /// Cancel spawned connection task on drop. impl Drop for H2ConnectionInner { fn drop(&mut self) { - self.handle.abort(); + if self + .sender + .send_request(http::Request::new(()), true) + .is_err() + { + self.handle.abort(); + } } } @@ -398,9 +404,18 @@ where #[cfg(test)] mod test { - use std::net; + use std::{ + future::Future, + net, + pin::Pin, + task::{Context, Poll}, + time::{Duration, Instant}, + }; - use actix_rt::net::TcpStream; + use actix_rt::{ + net::TcpStream, + time::{interval, Interval}, + }; use super::*; @@ -424,9 +439,36 @@ mod test { drop(conn); - match sender.ready().await { - Ok(_) => panic!("connection should be gone and can not be ready"), - Err(e) => assert!(e.is_io()), - }; + struct DropCheck { + sender: h2::client::SendRequest, + interval: Interval, + start_from: Instant, + } + + impl Future for DropCheck { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.get_mut(); + match futures_core::ready!(this.sender.poll_ready(cx)) { + Ok(()) => { + if this.start_from.elapsed() > Duration::from_secs(10) { + panic!("connection should be gone and can not be ready"); + } else { + let _ = this.interval.poll_tick(cx); + Poll::Pending + } + } + Err(_) => Poll::Ready(()), + } + } + } + + DropCheck { + sender, + interval: interval(Duration::from_millis(100)), + start_from: Instant::now(), + } + .await; } }