aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorj-berman <justinberman@protonmail.com>2026-01-08 16:10:36 -0800
committerj-berman <justinberman@protonmail.com>2026-02-02 10:22:41 -0800
commitee9e4a49baae0319f6cab543979e221f251339a3 (patch)
treed6ddeb5dd24015a62d242f26b799d6ea646ebee4 /tests
parent4ce39e0c144a092415e31876bcb3cdaa43481272 (diff)
downloadmonzero-core-ee9e4a49baae0319f6cab543979e221f251339a3.tar.gz
monzero-core-ee9e4a49baae0319f6cab543979e221f251339a3.tar.xz
monzero-core-ee9e4a49baae0319f6cab543979e221f251339a3.zip
p2p: connection patches
- Make sure the server sends a complete response when the client includes the "Connection: close" header. - Make sure the server terminates in `m_strand` to avoid concurrent socket closure and ops processing.
Diffstat (limited to 'tests')
-rw-r--r--tests/unit_tests/epee_http_server.cpp68
1 files changed, 68 insertions, 0 deletions
diff --git a/tests/unit_tests/epee_http_server.cpp b/tests/unit_tests/epee_http_server.cpp
index 1d3b60d54..f2eb9e41e 100644
--- a/tests/unit_tests/epee_http_server.cpp
+++ b/tests/unit_tests/epee_http_server.cpp
@@ -198,3 +198,71 @@ TEST(http_server, private_ip_limit)
failed |= bool(error);
EXPECT_TRUE(failed);
}
+
+TEST(http_server, read_then_close)
+{
+ namespace http = boost::beast::http;
+
+ http_server server{};
+ server.dummy_size = 200000;
+ server.init(nullptr, "8080");
+ server.run(2, false); // need at least 2 threads to trigger issues
+
+ bool failed_read = false;
+ bool closed_all_connections = true;
+ for (std::size_t j = 0; j < 1000; ++j)
+ {
+ boost::system::error_code error{};
+ boost::asio::io_context context{};
+ boost::asio::ip::tcp::socket stream{context};
+ stream.connect(
+ boost::asio::ip::tcp::endpoint{
+ boost::asio::ip::make_address("127.0.0.1"), 8080
+ },
+ error
+ );
+ EXPECT_FALSE(bool(error));
+
+ http::request<http::string_body> req{http::verb::get, "/dummy", 11};
+ req.set(http::field::host, "127.0.0.1");
+ req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
+ req.set(http::field::connection, "close"); // tell server to close connection after sending all data to the client
+ req.body() = make_payload();
+ req.prepare_payload();
+
+ dummy::response payload{};
+ boost::beast::flat_buffer buffer;
+ http::response_parser<http::basic_string_body<char>> parser;
+ parser.body_limit(server.dummy_size + 1024);
+
+ http::write(stream, req, error);
+ EXPECT_FALSE(bool(error));
+
+ http::read(stream, buffer, parser, error);
+
+ // If the read fails, continue the loop still just to make sure the server can handle it
+ failed_read |= bool(error);
+ if (failed_read)
+ continue;
+ failed_read |= !(parser.is_done());
+ if (failed_read)
+ continue;
+ const auto res = parser.release();
+ failed_read |= res.result_int() != 200u
+ || !(epee::serialization::load_t_from_binary(payload, res.body()))
+ || (server.dummy_size != std::count(payload.payload.begin(), payload.payload.end(), 'f'));
+
+ // See if the server closes the connection after handling the resp
+ char buf[1];
+ stream.read_some(boost::asio::buffer(buf), error);
+ closed_all_connections &= error == boost::asio::error::eof;
+ }
+
+ // The client should have been able to read all data sent by the server across all requests
+ EXPECT_FALSE(failed_read);
+
+ // The server should have closed all connections
+ EXPECT_TRUE(closed_all_connections);
+
+ server.send_stop_signal();
+}