From 5dda7353faee15c3c2e937b28e044da9a81f1883 Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Mon, 15 Sep 2025 21:29:33 -0400 Subject: [PATCH] Add test for url accumulation bug --- tests/test_http_handler.cpp | 68 +++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tests/test_http_handler.cpp b/tests/test_http_handler.cpp index 369f1c1..97049d3 100644 --- a/tests/test_http_handler.cpp +++ b/tests/test_http_handler.cpp @@ -3,6 +3,7 @@ #include "http_handler.hpp" #include "server.hpp" +#include #include #include #include @@ -198,3 +199,70 @@ TEST_CASE("HTTP pipelined POST requests race condition") { server->shutdown(); runThread.join(); } + +TEST_CASE("HTTP URL split across multiple writes") { + weaseldb::Config config; + HttpHandler handler(config); + auto server = Server::create(config, handler, {}); + int fd = server->create_local_connection(); + + auto runThread = std::thread{[&]() { server->run(); }}; + + // Test URL accumulation by splitting the URL across multiple writes + // This would have caught the original bug where URL string_view pointed + // to llhttp's internal buffer that gets reused between writes + + // Split "GET /metrics HTTP/1.1\r\n" across multiple writes + std::string part1 = "GET /met"; + std::string part2 = "rics HTTP/1.1\r\n"; + std::string headers = "Host: localhost\r\n" + "Connection: close\r\n" + "\r\n"; + + // Write URL in two parts - this tests URL accumulation + int w1 = write(fd, part1.c_str(), part1.size()); + REQUIRE(w1 == static_cast(part1.size())); + + // Attempt to trigger separate llhttp parsing calls + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + + int w2 = write(fd, part2.c_str(), part2.size()); + REQUIRE(w2 == static_cast(part2.size())); + + int w3 = write(fd, headers.c_str(), headers.size()); + REQUIRE(w3 == static_cast(headers.size())); + + // Read response + char buf[4096]; + int total_read = 0; + bool found_metrics_response = false; + + while (total_read < 4000) { + int r = read(fd, buf + total_read, sizeof(buf) - total_read - 1); + if (r <= 0) + break; + total_read += r; + + buf[total_read] = '\0'; + std::string response(buf, total_read); + + // Check for successful metrics response (not 404) + if (response.find("HTTP/1.1 200 OK") != std::string::npos && + response.find("text/plain; version=0.0.4") != std::string::npos) { + found_metrics_response = true; + break; + } + + // Check for 404 which would indicate URL accumulation failed + if (response.find("HTTP/1.1 404") != std::string::npos) { + FAIL("Got 404 - URL accumulation failed, split URL was not properly " + "reconstructed"); + } + } + + REQUIRE(found_metrics_response); + + close(fd); + server->shutdown(); + runThread.join(); +}