mirror of
https://github.com/Icinga/icinga2.git
synced 2026-04-12 12:26:26 -04:00
Mostly unrelated change to add the network label to test suites already touched by other changes in this PR. The label is already used for the perfdata writer tests to make it easy to exclude tests that need a network connection when building and testing in a sandbox.
564 lines
18 KiB
C++
564 lines
18 KiB
C++
// SPDX-FileCopyrightText: 2025 Icinga GmbH <https://icinga.com>
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <BoostTestTargetConfig.h>
|
|
#include "base/base64.hpp"
|
|
#include "base/json.hpp"
|
|
#include "remote/httphandler.hpp"
|
|
#include "test/base-testloggerfixture.hpp"
|
|
#include "test/base-tlsstream-fixture.hpp"
|
|
#include "test/test-ctest.hpp"
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/beast/http.hpp>
|
|
#include <utility>
|
|
|
|
using namespace icinga;
|
|
using namespace boost::beast;
|
|
using namespace boost::unit_test_framework;
|
|
|
|
struct HttpServerConnectionFixture : TlsStreamFixture, ConfigurationCacheDirFixture, TestLoggerFixture
|
|
{
|
|
HttpServerConnection::Ptr m_Connection;
|
|
StoppableWaitGroup::Ptr m_WaitGroup;
|
|
|
|
HttpServerConnectionFixture() : m_WaitGroup(new StoppableWaitGroup) {}
|
|
|
|
static void CreateApiListener(const String& allowOrigin)
|
|
{
|
|
ScriptGlobal::Set("NodeName", "server");
|
|
ApiListener::Ptr listener = new ApiListener;
|
|
listener->OnConfigLoaded();
|
|
listener->SetAccessControlAllowOrigin(new Array{allowOrigin});
|
|
}
|
|
|
|
static void CreateTestUsers()
|
|
{
|
|
ApiUser::Ptr user = new ApiUser;
|
|
user->SetName("client");
|
|
user->SetClientCN("client");
|
|
user->SetPermissions(new Array{"*"});
|
|
user->Register();
|
|
|
|
user = new ApiUser;
|
|
user->SetName("test");
|
|
user->SetPassword("test");
|
|
user->SetPermissions(new Array{"*"});
|
|
user->Register();
|
|
}
|
|
|
|
void SetupHttpServerConnection(bool authenticated, std::chrono::milliseconds livenessTimeout = std::chrono::milliseconds(10000))
|
|
{
|
|
String identity = authenticated ? "client" : "invalid";
|
|
m_Connection = new HttpServerConnection(m_WaitGroup, identity, authenticated, server);
|
|
m_Connection->SetLivenessTimeout(livenessTimeout);
|
|
m_Connection->Start();
|
|
}
|
|
|
|
template<class Rep, class Period>
|
|
bool AssertServerDisconnected(const std::chrono::duration<Rep, Period>& timeout)
|
|
{
|
|
std::size_t iterations = timeout / std::chrono::milliseconds(50);
|
|
for (std::size_t i = 0; i < iterations && !m_Connection->Disconnected(); i++) {
|
|
Utility::Sleep(std::chrono::duration<double>(timeout).count() / iterations);
|
|
}
|
|
return m_Connection->Disconnected();
|
|
}
|
|
};
|
|
|
|
class UnitTestHandler final : public HttpHandler
|
|
{
|
|
public:
|
|
using TestFn = std::function<void(HttpApiResponse& response, const boost::asio::yield_context&)>;
|
|
|
|
static void RegisterTestFn(std::string handle, TestFn fn) { testFns[std::move(handle)] = std::move(fn); }
|
|
|
|
private:
|
|
bool HandleRequest(const WaitGroup::Ptr&, const HttpApiRequest& request, HttpApiResponse& response,
|
|
boost::asio::yield_context& yc) override
|
|
{
|
|
response.result(boost::beast::http::status::ok);
|
|
|
|
auto path = request.Url()->GetPath();
|
|
|
|
if (path.size() == 3) {
|
|
if (auto it = testFns.find(path[2].GetData()); it != testFns.end()) {
|
|
it->second(response, yc);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
response.body() << "test";
|
|
return true;
|
|
}
|
|
|
|
static inline std::unordered_map<std::string, TestFn> testFns;
|
|
};
|
|
|
|
REGISTER_URLHANDLER("/v1/test", UnitTestHandler);
|
|
|
|
BOOST_FIXTURE_TEST_SUITE(remote_httpserverconnection, HttpServerConnectionFixture,
|
|
*CTestProperties("FIXTURES_REQUIRED ssl_certs")
|
|
*boost::unit_test::label("network")
|
|
*boost::unit_test::label("http"))
|
|
|
|
BOOST_AUTO_TEST_CASE(expect_100_continue)
|
|
{
|
|
CreateTestUsers();
|
|
SetupHttpServerConnection(true);
|
|
|
|
http::request<boost::beast::http::string_body> request;
|
|
request.method(http::verb::get);
|
|
request.version(11);
|
|
request.target("/v1/test");
|
|
request.set(http::field::expect, "100-continue");
|
|
request.set(http::field::host, "localhost:5665");
|
|
request.set(http::field::accept, "application/json");
|
|
request.set(http::field::connection, "close");
|
|
request.content_length(0);
|
|
http::request_serializer<http::string_body> sr(request);
|
|
http::write_header(*client, sr);
|
|
client->flush();
|
|
|
|
flat_buffer buf;
|
|
http::response<http::string_body> response;
|
|
BOOST_REQUIRE_NO_THROW(http::read(*client, buf, response));
|
|
|
|
BOOST_REQUIRE_EQUAL(response.version(), 11);
|
|
BOOST_REQUIRE_EQUAL(response.result(), http::status::continue_);
|
|
|
|
http::write(*client, sr);
|
|
client->flush();
|
|
|
|
BOOST_REQUIRE_NO_THROW(http::read(*client, buf, response));
|
|
BOOST_REQUIRE_EQUAL(response.version(), 11);
|
|
BOOST_REQUIRE_EQUAL(response.result(), http::status::ok);
|
|
BOOST_REQUIRE_EQUAL(response.body(), "test");
|
|
|
|
BOOST_REQUIRE(Shutdown(client));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(bad_request)
|
|
{
|
|
CreateTestUsers();
|
|
SetupHttpServerConnection(true);
|
|
|
|
http::request<boost::beast::http::string_body> request;
|
|
request.method(http::verb::get);
|
|
request.version(12);
|
|
request.target("/v1/test");
|
|
request.set(http::field::host, "localhost:5665");
|
|
request.set(http::field::accept, "application/json");
|
|
request.set(http::field::connection, "close");
|
|
request.content_length(0);
|
|
http::write(*client, request);
|
|
client->flush();
|
|
|
|
flat_buffer buf;
|
|
http::response<http::string_body> response;
|
|
BOOST_REQUIRE_NO_THROW(http::read(*client, buf, response));
|
|
|
|
BOOST_REQUIRE_EQUAL(response.result(), http::status::bad_request);
|
|
BOOST_REQUIRE_NE(response.body().find("<h1>Bad Request</h1>"), std::string::npos);
|
|
|
|
BOOST_REQUIRE(Shutdown(client));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(error_access_control)
|
|
{
|
|
CreateTestUsers();
|
|
CreateApiListener("example.org");
|
|
SetupHttpServerConnection(true);
|
|
|
|
http::request<boost::beast::http::string_body> request;
|
|
request.method(http::verb::options);
|
|
request.target("/v1/test");
|
|
request.set(http::field::origin, "example.org");
|
|
request.set(http::field::host, "localhost:5665");
|
|
request.set(http::field::access_control_request_method, "GET");
|
|
request.set(http::field::connection, "close");
|
|
request.content_length(0);
|
|
http::write(*client, request);
|
|
client->flush();
|
|
|
|
flat_buffer buf;
|
|
http::response<http::string_body> response;
|
|
BOOST_REQUIRE_NO_THROW(http::read(*client, buf, response));
|
|
|
|
BOOST_REQUIRE_EQUAL(response.version(), 11);
|
|
BOOST_REQUIRE_EQUAL(response.result(), http::status::ok);
|
|
BOOST_REQUIRE_EQUAL(response.body(), "Preflight OK");
|
|
|
|
BOOST_REQUIRE_EQUAL(response[http::field::access_control_allow_credentials], "true");
|
|
BOOST_REQUIRE_EQUAL(response[http::field::access_control_allow_origin], "example.org");
|
|
BOOST_REQUIRE_NE(response[http::field::access_control_allow_methods], "");
|
|
BOOST_REQUIRE_NE(response[http::field::access_control_allow_headers], "");
|
|
|
|
BOOST_REQUIRE(Shutdown(client));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(error_accept_header)
|
|
{
|
|
CreateTestUsers();
|
|
SetupHttpServerConnection(true);
|
|
|
|
http::request<boost::beast::http::string_body> request;
|
|
request.method(http::verb::post);
|
|
request.target("/v1/test");
|
|
request.set(http::field::host, "localhost:5665");
|
|
request.set(http::field::accept, "text/html");
|
|
request.set(http::field::connection, "close");
|
|
request.content_length(0);
|
|
http::write(*client, request);
|
|
client->flush();
|
|
|
|
flat_buffer buf;
|
|
http::response<http::string_body> response;
|
|
BOOST_REQUIRE_NO_THROW(http::read(*client, buf, response));
|
|
|
|
BOOST_REQUIRE_EQUAL(response.version(), 11);
|
|
BOOST_REQUIRE_EQUAL(response.result(), http::status::bad_request);
|
|
BOOST_REQUIRE_EQUAL(response.body(), "<h1>Accept header is missing or not set to 'application/json'.</h1>");
|
|
|
|
BOOST_REQUIRE(Shutdown(client));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(authenticate_cn)
|
|
{
|
|
CreateTestUsers();
|
|
SetupHttpServerConnection(true);
|
|
|
|
http::request<boost::beast::http::string_body> request;
|
|
request.method(http::verb::get);
|
|
request.target("/v1/test");
|
|
request.set(http::field::host, "localhost:5665");
|
|
request.set(http::field::accept, "application/json");
|
|
request.set(http::field::connection, "close");
|
|
request.content_length(0);
|
|
http::write(*client, request);
|
|
client->flush();
|
|
|
|
flat_buffer buf;
|
|
http::response<http::string_body> response;
|
|
BOOST_REQUIRE_NO_THROW(http::read(*client, buf, response));
|
|
|
|
BOOST_REQUIRE_EQUAL(response.version(), 11);
|
|
BOOST_REQUIRE_EQUAL(response.result(), http::status::ok);
|
|
|
|
BOOST_REQUIRE(Shutdown(client));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(authenticate_passwd)
|
|
{
|
|
CreateTestUsers();
|
|
SetupHttpServerConnection(false);
|
|
|
|
http::request<boost::beast::http::string_body> request;
|
|
request.method(http::verb::get);
|
|
request.target("/v1/test");
|
|
request.set(http::field::authorization, "Basic " + Base64::Encode("test:test"));
|
|
request.set(http::field::host, "localhost:5665");
|
|
request.set(http::field::accept, "application/json");
|
|
request.set(http::field::connection, "close");
|
|
request.content_length(0);
|
|
http::write(*client, request);
|
|
client->flush();
|
|
|
|
flat_buffer buf;
|
|
http::response<http::string_body> response;
|
|
BOOST_REQUIRE_NO_THROW(http::read(*client, buf, response));
|
|
|
|
BOOST_REQUIRE_EQUAL(response.version(), 11);
|
|
BOOST_REQUIRE_EQUAL(response.result(), http::status::ok);
|
|
|
|
BOOST_REQUIRE(Shutdown(client));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(authenticate_error_wronguser)
|
|
{
|
|
CreateTestUsers();
|
|
SetupHttpServerConnection(false);
|
|
|
|
http::request<boost::beast::http::string_body> request;
|
|
request.method(http::verb::get);
|
|
request.target("/v1/test");
|
|
request.set(http::field::authorization, "Basic " + Base64::Encode("invalid:invalid"));
|
|
request.set(http::field::host, "localhost:5665");
|
|
request.set(http::field::accept, "application/json");
|
|
request.set(http::field::connection, "close");
|
|
request.content_length(0);
|
|
http::write(*client, request);
|
|
client->flush();
|
|
|
|
flat_buffer buf;
|
|
http::response<http::string_body> response;
|
|
BOOST_REQUIRE_NO_THROW(http::read(*client, buf, response));
|
|
|
|
BOOST_REQUIRE_EQUAL(response.version(), 11);
|
|
BOOST_REQUIRE_EQUAL(response.result(), http::status::unauthorized);
|
|
Dictionary::Ptr body = JsonDecode(response.body());
|
|
BOOST_REQUIRE(body);
|
|
BOOST_REQUIRE_EQUAL(body->Get("error"), 401);
|
|
|
|
BOOST_REQUIRE(Shutdown(client));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(authenticate_error_wrongpasswd)
|
|
{
|
|
CreateTestUsers();
|
|
SetupHttpServerConnection(false);
|
|
|
|
http::request<boost::beast::http::string_body> request;
|
|
request.method(http::verb::get);
|
|
request.target("/v1/test");
|
|
request.set(http::field::authorization, "Basic " + Base64::Encode("test:invalid"));
|
|
request.set(http::field::host, "localhost:5665");
|
|
request.set(http::field::accept, "application/json");
|
|
request.set(http::field::connection, "close");
|
|
request.content_length(0);
|
|
http::write(*client, request);
|
|
client->flush();
|
|
|
|
flat_buffer buf;
|
|
http::response<http::string_body> response;
|
|
BOOST_REQUIRE_NO_THROW(http::read(*client, buf, response));
|
|
|
|
BOOST_REQUIRE_EQUAL(response.version(), 11);
|
|
BOOST_REQUIRE_EQUAL(response.result(), http::status::unauthorized);
|
|
Dictionary::Ptr body = JsonDecode(response.body());
|
|
BOOST_REQUIRE(body);
|
|
BOOST_REQUIRE_EQUAL(body->Get("error"), 401);
|
|
|
|
BOOST_REQUIRE(Shutdown(client));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(reuse_connection)
|
|
{
|
|
CreateTestUsers();
|
|
SetupHttpServerConnection(true);
|
|
|
|
http::request<boost::beast::http::string_body> request;
|
|
request.method(http::verb::get);
|
|
request.target("/v1/test");
|
|
request.set(http::field::host, "localhost:5665");
|
|
request.set(http::field::accept, "application/json");
|
|
request.keep_alive(true);
|
|
http::write(*client, request);
|
|
client->flush();
|
|
|
|
flat_buffer buf;
|
|
http::response<http::string_body> response;
|
|
BOOST_REQUIRE_NO_THROW(http::read(*client, buf, response));
|
|
|
|
BOOST_REQUIRE_EQUAL(response.version(), 11);
|
|
BOOST_REQUIRE_EQUAL(response.result(), http::status::ok);
|
|
BOOST_REQUIRE_EQUAL(response.body(), "test");
|
|
|
|
request.keep_alive(false);
|
|
http::write(*client, request);
|
|
client->flush();
|
|
|
|
boost::system::error_code ec;
|
|
http::response_parser<http::string_body> parser;
|
|
BOOST_REQUIRE_NO_THROW(http::read(*client, buf, parser));
|
|
|
|
BOOST_REQUIRE(parser.is_header_done());
|
|
BOOST_REQUIRE(parser.is_done());
|
|
BOOST_REQUIRE_EQUAL(parser.get().version(), 11);
|
|
BOOST_REQUIRE_EQUAL(parser.get().result(), http::status::ok);
|
|
BOOST_REQUIRE_EQUAL(parser.get().body(), "test");
|
|
|
|
// Second read to get the end of stream error;
|
|
http::read(*client, buf, response, ec);
|
|
BOOST_REQUIRE_EQUAL(ec, boost::system::error_code{boost::beast::http::error::end_of_stream});
|
|
|
|
BOOST_REQUIRE(AssertServerDisconnected(std::chrono::seconds(5)));
|
|
BOOST_REQUIRE(Shutdown(client));
|
|
BOOST_REQUIRE(ExpectLogPattern("HTTP client disconnected .*", std::chrono::seconds(5)));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(wg_abort)
|
|
{
|
|
CreateTestUsers();
|
|
SetupHttpServerConnection(true);
|
|
|
|
UnitTestHandler::RegisterTestFn("wgjoin", [this](HttpApiResponse& response, const boost::asio::yield_context&) {
|
|
response.body() << "test";
|
|
m_WaitGroup->Join();
|
|
});
|
|
|
|
http::request<boost::beast::http::string_body> request;
|
|
request.method(http::verb::get);
|
|
request.target("/v1/test/wgjoin");
|
|
request.set(http::field::host, "localhost:5665");
|
|
request.set(http::field::accept, "application/json");
|
|
request.keep_alive(true);
|
|
http::write(*client, request);
|
|
client->flush();
|
|
|
|
flat_buffer buf;
|
|
http::response_parser<http::string_body> parser;
|
|
BOOST_REQUIRE_NO_THROW(http::read(*client, buf, parser));
|
|
|
|
BOOST_REQUIRE(parser.is_header_done());
|
|
BOOST_REQUIRE(parser.is_done());
|
|
BOOST_REQUIRE_EQUAL(parser.get().version(), 11);
|
|
BOOST_REQUIRE_EQUAL(parser.get().result(), http::status::ok);
|
|
BOOST_REQUIRE_EQUAL(parser.get().body(), "test");
|
|
|
|
// Second read to get the end of stream error;
|
|
http::response<http::string_body> response{};
|
|
boost::system::error_code ec;
|
|
http::read(*client, buf, response, ec);
|
|
BOOST_REQUIRE_EQUAL(ec, boost::system::error_code{boost::beast::http::error::end_of_stream});
|
|
|
|
BOOST_REQUIRE(AssertServerDisconnected(std::chrono::seconds(5)));
|
|
BOOST_REQUIRE(Shutdown(client));
|
|
BOOST_REQUIRE(ExpectLogPattern("HTTP client disconnected .*", std::chrono::seconds(5)));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(client_shutdown)
|
|
{
|
|
CreateTestUsers();
|
|
SetupHttpServerConnection(true);
|
|
|
|
UnitTestHandler::RegisterTestFn("stream", [](HttpApiResponse& response, const boost::asio::yield_context& yc) {
|
|
response.StartStreaming(false);
|
|
response.Flush(yc);
|
|
|
|
boost::asio::deadline_timer dt{IoEngine::Get().GetIoContext()};
|
|
for (;;) {
|
|
dt.expires_from_now(boost::posix_time::seconds(1));
|
|
dt.async_wait(yc);
|
|
|
|
if (!response.IsClientDisconnected()) {
|
|
return;
|
|
}
|
|
|
|
response.body() << "test";
|
|
response.Flush(yc);
|
|
}
|
|
});
|
|
|
|
http::request<boost::beast::http::string_body> request;
|
|
request.method(http::verb::get);
|
|
request.target("/v1/test/stream");
|
|
request.set(http::field::host, "localhost:5665");
|
|
request.set(http::field::accept, "application/json");
|
|
request.keep_alive(true);
|
|
http::write(*client, request);
|
|
client->flush();
|
|
|
|
flat_buffer buf;
|
|
http::response_parser<http::string_body> parser;
|
|
BOOST_REQUIRE_NO_THROW(http::read_header(*client, buf, parser));
|
|
BOOST_REQUIRE(parser.is_header_done());
|
|
|
|
/* Unlike the other test cases we don't require success here, because with the request
|
|
* above, UnitTestHandler simulates a HttpHandler that is constantly writing.
|
|
* That may cause the shutdown to fail on the client-side with "application data after
|
|
* close notify", but the important part is that HttpServerConnection actually closes
|
|
* the connection on its own side, which we check with the BOOST_REQUIRE() below.
|
|
*/
|
|
BOOST_WARN(Shutdown(client));
|
|
|
|
BOOST_REQUIRE(AssertServerDisconnected(std::chrono::seconds(5)));
|
|
BOOST_REQUIRE(ExpectLogPattern("HTTP client disconnected .*", std::chrono::seconds(5)));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(handler_throw_error)
|
|
{
|
|
CreateTestUsers();
|
|
SetupHttpServerConnection(true);
|
|
|
|
UnitTestHandler::RegisterTestFn("throw", [](HttpApiResponse& response, const boost::asio::yield_context&) {
|
|
response.StartStreaming(false);
|
|
response.body() << "test";
|
|
|
|
boost::system::error_code ec{};
|
|
throw boost::system::system_error(ec);
|
|
});
|
|
|
|
http::request<boost::beast::http::string_body> request;
|
|
request.method(http::verb::get);
|
|
request.target("/v1/test/throw");
|
|
request.set(http::field::host, "localhost:5665");
|
|
request.set(http::field::accept, "application/json");
|
|
request.keep_alive(false);
|
|
http::write(*client, request);
|
|
client->flush();
|
|
|
|
flat_buffer buf;
|
|
http::response<http::string_body> response;
|
|
BOOST_REQUIRE_NO_THROW(http::read(*client, buf, response));
|
|
|
|
BOOST_REQUIRE_EQUAL(response.version(), 11);
|
|
BOOST_REQUIRE_EQUAL(response.result(), http::status::internal_server_error);
|
|
Dictionary::Ptr body = JsonDecode(response.body());
|
|
BOOST_REQUIRE(body);
|
|
BOOST_REQUIRE_EQUAL(body->Get("error"), 500);
|
|
BOOST_REQUIRE_EQUAL(body->Get("status"), "Unhandled exception");
|
|
|
|
BOOST_REQUIRE(Shutdown(client));
|
|
BOOST_REQUIRE(ExpectLogPattern("HTTP client disconnected .*", std::chrono::seconds(5)));
|
|
BOOST_REQUIRE(!ExpectLogPattern("Exception while processing HTTP request.*"));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(handler_throw_streaming)
|
|
{
|
|
CreateTestUsers();
|
|
SetupHttpServerConnection(true);
|
|
|
|
UnitTestHandler::RegisterTestFn("throw", [](HttpApiResponse& response, const boost::asio::yield_context& yc) {
|
|
response.StartStreaming(false);
|
|
response.body() << "test";
|
|
|
|
response.Flush(yc);
|
|
|
|
boost::system::error_code ec{};
|
|
throw boost::system::system_error(ec);
|
|
});
|
|
|
|
http::request<boost::beast::http::string_body> request;
|
|
request.method(http::verb::get);
|
|
request.target("/v1/test/throw");
|
|
request.set(http::field::host, "localhost:5665");
|
|
request.set(http::field::accept, "application/json");
|
|
request.keep_alive(true);
|
|
|
|
http::write(*client, request);
|
|
client->flush();
|
|
|
|
flat_buffer buf;
|
|
http::response_parser<http::string_body> parser;
|
|
boost::system::error_code ec;
|
|
http::read(*client, buf, parser, ec);
|
|
|
|
/* Since the handler threw in the middle of sending the message we shouldn't be able
|
|
* to read a complete message here.
|
|
*/
|
|
BOOST_REQUIRE_EQUAL(ec, boost::system::error_code{boost::beast::http::error::partial_message});
|
|
|
|
/* The body should only contain the single "test" the handler has written, without any
|
|
* attempts made to additionally write some json error message.
|
|
*/
|
|
BOOST_REQUIRE_EQUAL(parser.get().body(), "test");
|
|
|
|
/* We then expect the server to initiate a shutdown, which we then complete below.
|
|
*/
|
|
BOOST_REQUIRE(AssertServerDisconnected(std::chrono::seconds(5)));
|
|
BOOST_REQUIRE(Shutdown(client));
|
|
BOOST_REQUIRE(ExpectLogPattern("HTTP client disconnected .*", std::chrono::seconds(5)));
|
|
BOOST_REQUIRE(ExpectLogPattern("Exception while processing HTTP request.*"));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(liveness_disconnect)
|
|
{
|
|
SetupHttpServerConnection(false, std::chrono::milliseconds(300)); // 300ms liveness timeout is more than enough!
|
|
|
|
BOOST_REQUIRE(AssertServerDisconnected(std::chrono::milliseconds(450))); // Give some leeway to Asio's timers
|
|
BOOST_REQUIRE(ExpectLogPattern("HTTP client disconnected .*"));
|
|
BOOST_REQUIRE(ExpectLogPattern("No messages for HTTP connection have been received in the last \\d+ seconds, disconnecting .*"));
|
|
BOOST_REQUIRE(Shutdown(client));
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|