From dd8071a7d496234847f401fe619b58788d67179e Mon Sep 17 00:00:00 2001 From: yhirose Date: Tue, 17 Mar 2026 17:07:46 -0400 Subject: [PATCH] Fix #2397 --- test/test.cc | 57 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/test/test.cc b/test/test.cc index 776d704b..e7fcac8a 100644 --- a/test/test.cc +++ b/test/test.cc @@ -13827,9 +13827,9 @@ TEST_F(OpenStreamTest, ProhibitedTrailersAreIgnored_Stream) { EXPECT_EQ(std::string(""), handle.response->get_header_value("X-Allowed")); } -static std::thread serve_single_response(int port, +static std::thread serve_single_response(std::promise &port_promise, const std::string &response) { - return std::thread([port, response] { + return std::thread([&port_promise, response] { auto srv = ::socket(AF_INET, SOCK_STREAM, 0); default_socket_options(srv); detail::set_socket_opt_time(srv, SOL_SOCKET, SO_RCVTIMEO, 5, 0); @@ -13837,7 +13837,7 @@ static std::thread serve_single_response(int port, sockaddr_in addr{}; addr.sin_family = AF_INET; - addr.sin_port = htons(static_cast(port)); + addr.sin_port = htons(0); // Let OS assign a free port ::inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); int opt = 1; @@ -13849,8 +13849,16 @@ static std::thread serve_single_response(int port, #endif sizeof(opt)); - ::bind(srv, reinterpret_cast(&addr), sizeof(addr)); - ::listen(srv, 1); + if (::bind(srv, reinterpret_cast(&addr), sizeof(addr)) != 0 || + ::listen(srv, 1) != 0) { + port_promise.set_value(-1); + detail::close_socket(srv); + return; + } + + socklen_t addr_len = sizeof(addr); + ::getsockname(srv, reinterpret_cast(&addr), &addr_len); + port_promise.set_value(static_cast(ntohs(addr.sin_port))); sockaddr_in cli_addr{}; socklen_t cli_len = sizeof(cli_addr); @@ -13880,17 +13888,19 @@ TEST(OpenStreamMalformedContentLength, InvalidArgument) { signal(SIGPIPE, SIG_IGN); #endif + std::promise port_promise; + auto port_future = port_promise.get_future(); auto server_thread = - serve_single_response(PORT + 2, "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n" - "Content-Length: not-a-number\r\n" - "Connection: close\r\n" - "\r\n" - "hello"); + serve_single_response(port_promise, "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: not-a-number\r\n" + "Connection: close\r\n" + "\r\n" + "hello"); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - - Client cli("127.0.0.1", PORT + 2); + auto port = port_future.get(); + ASSERT_GT(port, 0); + Client cli("127.0.0.1", port); auto handle = cli.open_stream("GET", "/"); EXPECT_FALSE(handle.is_valid()); @@ -13902,21 +13912,24 @@ TEST(OpenStreamMalformedContentLength, OutOfRange) { signal(SIGPIPE, SIG_IGN); #endif + std::promise port_promise; + auto port_future = port_promise.get_future(); auto server_thread = serve_single_response( - PORT + 4, "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n" - "Content-Length: 99999999999999999999999999\r\n" - "Connection: close\r\n" - "\r\n" - "hello"); + port_promise, "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 99999999999999999999999999\r\n" + "Connection: close\r\n" + "\r\n" + "hello"); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + auto port = port_future.get(); + ASSERT_GT(port, 0); // Before the fix, std::stoull would throw std::out_of_range here and // crash the process. After the fix, strtoull silently clamps to // ULLONG_MAX so the stream opens without crashing. The important thing // is that the process does NOT terminate. - Client cli("127.0.0.1", PORT + 4); + Client cli("127.0.0.1", port); auto handle = cli.open_stream("GET", "/"); EXPECT_TRUE(handle.is_valid());