Add test for pipeline request parsing bug
This commit is contained in:
@@ -207,6 +207,12 @@ add_executable(test_metric tests/test_metric.cpp)
|
|||||||
target_link_libraries(test_metric doctest_impl weaseldb_sources_debug)
|
target_link_libraries(test_metric doctest_impl weaseldb_sources_debug)
|
||||||
target_compile_options(test_metric PRIVATE -UNDEBUG)
|
target_compile_options(test_metric PRIVATE -UNDEBUG)
|
||||||
|
|
||||||
|
# HTTP handler test
|
||||||
|
add_executable(test_http_handler tests/test_http_handler.cpp)
|
||||||
|
target_link_libraries(test_http_handler doctest_impl weaseldb_sources_debug)
|
||||||
|
target_compile_options(test_http_handler PRIVATE -UNDEBUG)
|
||||||
|
add_test(NAME test_http_handler COMMAND test_http_handler)
|
||||||
|
|
||||||
# Register with CTest
|
# Register with CTest
|
||||||
add_test(NAME metric_tests COMMAND test_metric)
|
add_test(NAME metric_tests COMMAND test_metric)
|
||||||
|
|
||||||
|
|||||||
81
tests/test_http_handler.cpp
Normal file
81
tests/test_http_handler.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include "config.hpp"
|
||||||
|
#include "connection.hpp"
|
||||||
|
#include "http_handler.hpp"
|
||||||
|
#include "server.hpp"
|
||||||
|
|
||||||
|
#include <doctest/doctest.h>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
TEST_CASE("HTTP pipelined POST requests race condition") {
|
||||||
|
weaseldb::Config config;
|
||||||
|
HttpHandler handler(config);
|
||||||
|
auto server = Server::create(config, handler, {});
|
||||||
|
int fd = server->create_local_connection();
|
||||||
|
|
||||||
|
auto runThread = std::thread{[&]() { server->run(); }};
|
||||||
|
|
||||||
|
// Create a POST request with JSON body that requires parsing
|
||||||
|
std::string json_body = R"({
|
||||||
|
"request_id": "test-123",
|
||||||
|
"leader_id": "leader-1",
|
||||||
|
"read_version": 1,
|
||||||
|
"preconditions": [],
|
||||||
|
"operations": [{"write": {"key": "dGVzdA==", "value": "dmFsdWU="}}]
|
||||||
|
})";
|
||||||
|
|
||||||
|
std::string first_post = "POST /v1/commit HTTP/1.1\r\n"
|
||||||
|
"Host: localhost\r\n"
|
||||||
|
"Content-Type: application/json\r\n"
|
||||||
|
"Content-Length: " +
|
||||||
|
std::to_string(json_body.size()) +
|
||||||
|
"\r\n"
|
||||||
|
"Connection: keep-alive\r\n"
|
||||||
|
"\r\n" +
|
||||||
|
json_body;
|
||||||
|
|
||||||
|
std::string second_get = "GET /v1/version HTTP/1.1\r\n"
|
||||||
|
"Host: localhost\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"\r\n";
|
||||||
|
|
||||||
|
// Send POST request followed immediately by GET request
|
||||||
|
// This creates a scenario where the GET request starts parsing
|
||||||
|
// while the POST response is being written (triggering the reset)
|
||||||
|
int w1 = write(fd, first_post.c_str(), first_post.size());
|
||||||
|
REQUIRE(w1 == static_cast<int>(first_post.size()));
|
||||||
|
|
||||||
|
int w2 = write(fd, second_get.c_str(), second_get.size());
|
||||||
|
REQUIRE(w2 == static_cast<int>(second_get.size()));
|
||||||
|
|
||||||
|
// Read responses using blocking I/O (deterministic synchronization)
|
||||||
|
char buf[4096];
|
||||||
|
int total_read = 0;
|
||||||
|
int responses_found = 0;
|
||||||
|
|
||||||
|
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);
|
||||||
|
std::size_t pos = 0;
|
||||||
|
while ((pos = response.find("HTTP/1.1", pos)) != std::string::npos) {
|
||||||
|
responses_found++;
|
||||||
|
pos += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (responses_found >= 2)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should get responses to both requests
|
||||||
|
// Race condition might cause parsing errors or connection issues
|
||||||
|
CHECK(responses_found >= 1); // At minimum should handle first request
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
server->shutdown();
|
||||||
|
runThread.join();
|
||||||
|
}
|
||||||
@@ -25,7 +25,6 @@ struct EchoHandler : ConnectionHandler {
|
|||||||
TEST_CASE("Echo test") {
|
TEST_CASE("Echo test") {
|
||||||
EchoHandler handler;
|
EchoHandler handler;
|
||||||
weaseldb::Config config;
|
weaseldb::Config config;
|
||||||
config.server.io_threads = 1;
|
|
||||||
auto server = Server::create(config, handler, {});
|
auto server = Server::create(config, handler, {});
|
||||||
int fd = server->create_local_connection();
|
int fd = server->create_local_connection();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user