diff --git a/Cargo.lock b/Cargo.lock index 66d6fb11b..238736c56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,7 +49,7 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.13.0" +version = "3.13.1" dependencies = [ "actix-codec", "actix-http-test", diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index ff1789aea..46b3165f5 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -2,6 +2,12 @@ ## Unreleased +## 3.13.1 + +- Fix HTTP/1 WebSocket upgrade responses being overwritten with `Connection: close` when the upgraded request payload remains open. [#4115] + +[#4115]: https://github.com/actix/actix-web/issues/4115 + ## 3.13.0 - When configured, gracefully close HTTP/1 connections after early responses to unread request bodies. [#3967] diff --git a/actix-http/Cargo.toml b/actix-http/Cargo.toml index bd6a726de..6b9d5c137 100644 --- a/actix-http/Cargo.toml +++ b/actix-http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-http" -version = "3.13.0" +version = "3.13.1" authors = ["Nikolay Kim ", "Rob Ede "] description = "HTTP types and services for the Actix ecosystem" keywords = ["actix", "http", "framework", "async", "futures"] diff --git a/actix-http/README.md b/actix-http/README.md index 421c904bb..d75824a22 100644 --- a/actix-http/README.md +++ b/actix-http/README.md @@ -5,11 +5,11 @@ [![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.12.0)](https://docs.rs/actix-http/3.12.0) +[![Documentation](https://docs.rs/actix-http/badge.svg?version=3.13.1)](https://docs.rs/actix-http/3.13.1) ![Version](https://img.shields.io/badge/rustc-1.88+-ab6000.svg) ![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-http.svg)
-[![dependency status](https://deps.rs/crate/actix-http/3.12.0/status.svg)](https://deps.rs/crate/actix-http/3.12.0) +[![dependency status](https://deps.rs/crate/actix-http/3.13.1/status.svg)](https://deps.rs/crate/actix-http/3.13.1) [![Download](https://img.shields.io/crates/d/actix-http.svg)](https://crates.io/crates/actix-http) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index f28e91f31..bfac0855c 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -451,7 +451,7 @@ where mut res: Response<()>, body: B, ) -> Result<(), DispatchError> { - let close_after_response = { + let close_after_response = !res.upgrade() && { let this = self.as_mut().project(); should_close_after_response(this.payload.as_ref(), *this.payload_drainable) }; @@ -492,7 +492,7 @@ where mut res: Response<()>, body: BoxBody, ) -> Result<(), DispatchError> { - let close_after_response = { + let close_after_response = !res.upgrade() && { let this = self.as_mut().project(); should_close_after_response(this.payload.as_ref(), *this.payload_drainable) }; diff --git a/actix-http/src/h1/dispatcher_tests.rs b/actix-http/src/h1/dispatcher_tests.rs index a9262a483..874ce85e6 100644 --- a/actix-http/src/h1/dispatcher_tests.rs +++ b/actix-http/src/h1/dispatcher_tests.rs @@ -228,6 +228,17 @@ fn ready_chunk_body_service( }) } +fn upgrade_response_service( +) -> impl Service, Error = Error> { + fn_service(|_req: Request| { + ready(Ok::<_, Error>( + Response::build(StatusCode::SWITCHING_PROTOCOLS) + .upgrade("websocket") + .finish(), + )) + }) +} + #[actix_rt::test] async fn late_request() { let mut buf = TestBuffer::empty(); @@ -1133,6 +1144,64 @@ async fn upgrade_handling() { .await; } +#[actix_rt::test] +async fn upgrade_response_does_not_close_unfinished_payload() { + let buf = TestSeqBuffer::new(http_msg( + r" + GET /ws HTTP/1.1 + Connection: Upgrade + Upgrade: websocket + Sec-WebSocket-Version: 13 + Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== + + ", + )); + + let services = HttpFlow::new( + upgrade_response_service(), + ExpectHandler, + None::, + ); + + let h1 = Dispatcher::new( + buf.clone(), + services, + ServiceConfig::default(), + None, + OnConnectData::default(), + ); + pin!(h1); + + lazy(|cx| { + assert!(h1.as_mut().poll(cx).is_pending()); + + let mut res = BytesMut::from(buf.take_write_buf().as_ref()); + stabilize_date_header(&mut res); + let res = &res[..]; + + let exp = http_msg( + r" + HTTP/1.1 101 Switching Protocols + connection: upgrade + upgrade: websocket + date: Thu, 01 Jan 1970 12:34:56 UTC + + ", + ); + + assert_eq!( + res, + exp, + "\nexpected response not in write buffer:\n\ + response: {:?}\n\ + expected: {:?}", + String::from_utf8_lossy(res), + String::from_utf8_lossy(&exp) + ); + }) + .await; +} + // fix in #2624 reverted temporarily // complete fix tracked in #2745 #[ignore] diff --git a/actix-web/Cargo.toml b/actix-web/Cargo.toml index 9cc91bc14..754f04880 100644 --- a/actix-web/Cargo.toml +++ b/actix-web/Cargo.toml @@ -137,7 +137,7 @@ actix-service = "2" actix-tls = { version = "3.4", default-features = false, optional = true } actix-utils = "3" -actix-http = "3.13.0" +actix-http = "3.13.1" actix-router = { version = "0.5.4", default-features = false, features = ["http"] } actix-web-codegen = { version = "4.3", optional = true, default-features = false } diff --git a/awc/Cargo.toml b/awc/Cargo.toml index a27345165..6ca429e61 100644 --- a/awc/Cargo.toml +++ b/awc/Cargo.toml @@ -98,7 +98,7 @@ dangerous-h2c = [] [dependencies] actix-codec = "0.5" -actix-http = { version = "3.13.0", features = ["http2", "ws"] } +actix-http = { version = "3.13.1", features = ["http2", "ws"] } actix-rt = { version = "2.1", default-features = false } actix-service = "2" actix-tls = { version = "3.4", features = ["connect", "uri"] }