From 4fd1b86b0dfbfcf3da6d598bbae01f87e962cc59 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 28 Mar 2026 21:03:13 +0900 Subject: [PATCH] chore: add test --- actix-web/CHANGES.md | 2 +- actix-web/src/server.rs | 9 +++--- actix-web/tests/test_httpserver.rs | 45 ++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/actix-web/CHANGES.md b/actix-web/CHANGES.md index cb51dbec8..72f541653 100644 --- a/actix-web/CHANGES.md +++ b/actix-web/CHANGES.md @@ -2,7 +2,7 @@ ## Unreleased -- Enable dual-stack IPv6 sockets on Windows so that binding to `[::]` also accepts IPv4 connections. +- Enable dual-stack IPv6 sockets on Windows when possible so that Actix-created listeners bound to `[::]` also accept IPv4 connections. - Panic when calling `Route::to()` or `Route::service()` after `Route::wrap()` to prevent silently dropping route middleware. [#3944] - Fix `HttpRequest::{match_pattern,match_name}` reporting path-only matches when route guards disambiguate overlapping resources. [#3346] - Fix `Readlines` handling of lines split across payload chunks so combined line limits are enforced and complete lines are yielded. diff --git a/actix-web/src/server.rs b/actix-web/src/server.rs index 3835b6ddb..59d182e0f 100644 --- a/actix-web/src/server.rs +++ b/actix-web/src/server.rs @@ -447,10 +447,11 @@ where /// /// # Dual-Stack IPv6 /// - /// On Windows, binding to an IPv6 address (e.g., `[::]:8080`) automatically enables dual-stack - /// mode, allowing the socket to accept both IPv4 and IPv6 connections. On Linux and macOS, - /// dual-stack is typically already the OS default. If you need IPv6-only behavior on Windows, - /// create the listener manually and pass it to [`listen()`](Self::listen()). + /// On Windows, when this method creates an IPv6 listener (e.g., for `[::]:8080`), this + /// attempts to enable dual-stack mode so the socket can accept both IPv4 and IPv6 + /// connections. On Linux and macOS, dual-stack is typically already the OS default. If you + /// need IPv6-only behavior on Windows, create the listener manually and pass it to + /// [`listen()`](Self::listen()). /// /// # Typical Usage /// diff --git a/actix-web/tests/test_httpserver.rs b/actix-web/tests/test_httpserver.rs index c4f87fabb..44283ebd4 100644 --- a/actix-web/tests/test_httpserver.rs +++ b/actix-web/tests/test_httpserver.rs @@ -217,3 +217,48 @@ async fn test_tcp_nodelay_enabled() { async fn test_tcp_nodelay_disabled() { assert_tcp_nodelay_config(false).await; } + +#[actix_rt::test] +#[cfg(windows)] +async fn test_dual_stack_ipv6_on_windows() { + let (tx, rx) = mpsc::channel(); + + thread::spawn(move || { + actix_rt::System::new() + .block_on(async { + let srv = HttpServer::new(|| { + App::new().service( + web::resource("/") + .route(web::to(|| async { HttpResponse::Ok().body("test") })), + ) + }) + .workers(1) + .disable_signals() + .bind("[::]:0") + .unwrap(); + + let port = srv.addrs()[0].port(); + let srv = srv.run(); + + tx.send((srv.handle(), port)).unwrap(); + srv.await + }) + .unwrap(); + }); + + let (srv, port) = rx.recv().unwrap(); + + let client = awc::Client::builder() + .connector(awc::Connector::new().timeout(Duration::from_secs(1))) + .finish(); + + let response = client + .get(format!("http://127.0.0.1:{port}")) + .send() + .await + .unwrap(); + + assert!(response.status().is_success()); + + srv.stop(false).await; +}