diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index a0c5830..f144565 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -578,29 +578,93 @@ void HTTPRequest::delete_(std::variant, std::visit( [&](auto&& arg) { - auto req {DeleteRequest::builder( - FactoryRequestWrapper::create(response, handlerType, shouldRun))}; - req.url(arg.url.url(), arg.secureCommunication) - .appendHeaders(arg.httpHeaders) - .timeout(timeout) - .userAgent(userAgent) - .outputFile(outputFile) - .execute(); + using T = std::decay_t; + if constexpr (std::is_same_v>) + { + auto req {DeleteRequest::builder( + FactoryRequestWrapper::create(response, handlerType, shouldRun))}; + req.url(arg.url.url(), arg.secureCommunication) + .template postData(arg.data) + .appendHeaders(arg.httpHeaders) + .timeout(timeout) + .userAgent(userAgent) + .outputFile(outputFile) + .execute(); - std::visit( - [&](auto&& arg) - { - using Tb = std::decay_t; - if constexpr (std::is_same_v>) + std::visit( + [&](auto&& arg) { - arg.onSuccess(response); - } - else if constexpr (std::is_same_v>) + using Tb = std::decay_t; + if constexpr (std::is_same_v>) + { + arg.onSuccess(response); + } + else if constexpr (std::is_same_v>) + { + arg.onSuccess(std::move(response)); + } + }, + postRequestParameters); + } + else if constexpr (std::is_same_v>) + { + auto req {DeleteRequest::builder( + FactoryRequestWrapper::create(response, handlerType, shouldRun))}; + req.url(arg.url.url(), arg.secureCommunication) + .template postData(arg.data) + .appendHeaders(arg.httpHeaders) + .timeout(timeout) + .userAgent(userAgent) + .outputFile(outputFile) + .execute(); + + std::visit( + [&](auto&& arg) { - arg.onSuccess(std::move(response)); - } - }, - postRequestParameters); + using Tb = std::decay_t; + if constexpr (std::is_same_v>) + { + arg.onSuccess(response); + } + else if constexpr (std::is_same_v>) + { + arg.onSuccess(std::move(response)); + } + }, + postRequestParameters); + } + else if constexpr (std::is_same_v>) + { + const std::string data = arg.data.dump(); + auto req {DeleteRequest::builder( + FactoryRequestWrapper::create(response, handlerType, shouldRun))}; + req.url(arg.url.url(), arg.secureCommunication) + .template postData(data) + .appendHeaders(arg.httpHeaders) + .timeout(timeout) + .userAgent(userAgent) + .outputFile(outputFile) + .execute(); + + std::visit( + [&](auto&& arg) + { + using Tb = std::decay_t; + if constexpr (std::is_same_v>) + { + arg.onSuccess(response); + } + else if constexpr (std::is_same_v>) + { + arg.onSuccess(std::move(response)); + } + }, + postRequestParameters); + } + else + { + throw std::runtime_error("Invalid type"); + } }, requestParameters); } diff --git a/src/UNIXSocketRequest.cpp b/src/UNIXSocketRequest.cpp index 2fb2ac8..1740593 100644 --- a/src/UNIXSocketRequest.cpp +++ b/src/UNIXSocketRequest.cpp @@ -42,6 +42,7 @@ void UNIXSocketRequest::download(std::variant, GetRequest::builder(FactoryRequestWrapper::create(response, handlerType, shouldRun)) .url(arg.url.url(), arg.secureCommunication) .unixSocketPath(arg.url.unixSocketPath()) + .appendHeaders(arg.httpHeaders) .timeout(timeout) .userAgent(userAgent) .outputFile(outputFile) @@ -102,6 +103,7 @@ void UNIXSocketRequest::post(std::variant, FactoryRequestWrapper::create(response, handlerType, shouldRun))}; req.url(arg.url.url(), arg.secureCommunication) .unixSocketPath(arg.url.unixSocketPath()) + .appendHeaders(arg.httpHeaders) .timeout(timeout) .userAgent(userAgent) .template postData(arg.data) @@ -128,6 +130,7 @@ void UNIXSocketRequest::post(std::variant, FactoryRequestWrapper::create(response, handlerType, shouldRun))}; req.url(arg.url.url(), arg.secureCommunication) .unixSocketPath(arg.url.unixSocketPath()) + .appendHeaders(arg.httpHeaders) .timeout(timeout) .userAgent(userAgent) .template postData(arg.data) @@ -156,6 +159,7 @@ void UNIXSocketRequest::post(std::variant, FactoryRequestWrapper::create(response, handlerType, shouldRun))}; req.url(arg.url.url(), arg.secureCommunication) .unixSocketPath(arg.url.unixSocketPath()) + .appendHeaders(arg.httpHeaders) .timeout(timeout) .userAgent(userAgent) .template postData(data) @@ -234,6 +238,7 @@ void UNIXSocketRequest::get(std::variant, GetRequest::builder(FactoryRequestWrapper::create(response, handlerType, shouldRun))}; req.url(arg.url.url(), arg.secureCommunication) .unixSocketPath(arg.url.unixSocketPath()) + .appendHeaders(arg.httpHeaders) .timeout(timeout) .userAgent(userAgent) .outputFile(outputFile) @@ -309,6 +314,7 @@ void UNIXSocketRequest::put(std::variant, FactoryRequestWrapper::create(response, handlerType, shouldRun))}; req.url(arg.url.url(), arg.secureCommunication) .unixSocketPath(arg.url.unixSocketPath()) + .appendHeaders(arg.httpHeaders) .timeout(timeout) .userAgent(userAgent) .template postData(arg.data) @@ -336,6 +342,7 @@ void UNIXSocketRequest::put(std::variant, FactoryRequestWrapper::create(response, handlerType, shouldRun))}; req.url(arg.url.url(), arg.secureCommunication) .unixSocketPath(arg.url.unixSocketPath()) + .appendHeaders(arg.httpHeaders) .timeout(timeout) .userAgent(userAgent) .template postData(arg.data) @@ -364,6 +371,7 @@ void UNIXSocketRequest::put(std::variant, FactoryRequestWrapper::create(response, handlerType, shouldRun))}; req.url(arg.url.url(), arg.secureCommunication) .template postData(data) + .unixSocketPath(arg.url.unixSocketPath()) .appendHeaders(arg.httpHeaders) .timeout(timeout) .userAgent(userAgent) @@ -445,6 +453,7 @@ void UNIXSocketRequest::patch(std::variant, FactoryRequestWrapper::create(response, handlerType, shouldRun))}; req.url(arg.url.url(), arg.secureCommunication) .unixSocketPath(arg.url.unixSocketPath()) + .appendHeaders(arg.httpHeaders) .timeout(timeout) .userAgent(userAgent) .template postData(arg.data) @@ -472,6 +481,7 @@ void UNIXSocketRequest::patch(std::variant, FactoryRequestWrapper::create(response, handlerType, shouldRun))}; req.url(arg.url.url(), arg.secureCommunication) .unixSocketPath(arg.url.unixSocketPath()) + .appendHeaders(arg.httpHeaders) .timeout(timeout) .userAgent(userAgent) .template postData(arg.data) @@ -500,6 +510,7 @@ void UNIXSocketRequest::patch(std::variant, FactoryRequestWrapper::create(response, handlerType, shouldRun))}; req.url(arg.url.url(), arg.secureCommunication) .unixSocketPath(arg.url.unixSocketPath()) + .appendHeaders(arg.httpHeaders) .timeout(timeout) .userAgent(userAgent) .template postData(data) @@ -574,29 +585,96 @@ void UNIXSocketRequest::delete_(std::variant, std::visit( [&](auto&& arg) { - auto req {DeleteRequest::builder( - FactoryRequestWrapper::create(response, handlerType, shouldRun))}; - req.url(arg.url.url(), arg.secureCommunication) - .unixSocketPath(arg.url.unixSocketPath()) - .timeout(timeout) - .userAgent(userAgent) - .outputFile(outputFile) - .execute(); + using T = std::decay_t; + if constexpr (std::is_same_v>) + { + auto req {DeleteRequest::builder( + FactoryRequestWrapper::create(response, handlerType, shouldRun))}; + req.url(arg.url.url(), arg.secureCommunication) + .unixSocketPath(arg.url.unixSocketPath()) + .appendHeaders(arg.httpHeaders) + .timeout(timeout) + .userAgent(userAgent) + .template postData(arg.data) + .outputFile(outputFile) + .execute(); - std::visit( - [&](auto&& arg) - { - using Tb = std::decay_t; - if constexpr (std::is_same_v>) + std::visit( + [&](auto&& arg) { - arg.onSuccess(response); - } - else if constexpr (std::is_same_v>) + using Tb = std::decay_t; + if constexpr (std::is_same_v>) + { + arg.onSuccess(response); + } + else if constexpr (std::is_same_v>) + { + arg.onSuccess(std::move(response)); + } + }, + postRequestParameters); + } + else if constexpr (std::is_same_v>) + { + auto req {DeleteRequest::builder( + FactoryRequestWrapper::create(response, handlerType, shouldRun))}; + req.url(arg.url.url(), arg.secureCommunication) + .unixSocketPath(arg.url.unixSocketPath()) + .appendHeaders(arg.httpHeaders) + .timeout(timeout) + .userAgent(userAgent) + .template postData(arg.data) + .outputFile(outputFile) + .execute(); + + std::visit( + [&](auto&& arg) { - arg.onSuccess(std::move(response)); - } - }, - postRequestParameters); + using Tb = std::decay_t; + if constexpr (std::is_same_v>) + { + arg.onSuccess(response); + } + else if constexpr (std::is_same_v>) + { + arg.onSuccess(std::move(response)); + } + }, + postRequestParameters); + } + else if constexpr (std::is_same_v>) + { + const auto data = arg.data.dump(); + auto req {DeleteRequest::builder( + FactoryRequestWrapper::create(response, handlerType, shouldRun))}; + req.url(arg.url.url(), arg.secureCommunication) + .unixSocketPath(arg.url.unixSocketPath()) + .template postData(data) + .appendHeaders(arg.httpHeaders) + .timeout(timeout) + .userAgent(userAgent) + .outputFile(outputFile) + .execute(); + + std::visit( + [&](auto&& arg) + { + using Tb = std::decay_t; + if constexpr (std::is_same_v>) + { + arg.onSuccess(response); + } + else if constexpr (std::is_same_v>) + { + arg.onSuccess(std::move(response)); + } + }, + postRequestParameters); + } + else + { + throw std::runtime_error("Invalid type"); + } }, requestParameters); } diff --git a/src/urlRequest.hpp b/src/urlRequest.hpp index 036d1ed..304f741 100644 --- a/src/urlRequest.hpp +++ b/src/urlRequest.hpp @@ -436,7 +436,7 @@ class GetRequest final : public cURLRequest /** * @brief This class is a wrapper for curl library. It provides a simple interface to perform HTTP DELETE requests. */ -class DeleteRequest final : public cURLRequest +class DeleteRequest final : public cURLRequest, public PostData { public: /** @@ -445,6 +445,7 @@ class DeleteRequest final : public cURLRequest */ explicit DeleteRequest(std::shared_ptr requestImplementator) : cURLRequest(requestImplementator) + , PostData(requestImplementator) { requestImplementator->setOptionString(OPT_CUSTOMREQUEST, METHOD_TYPE_MAP.at(METHOD_DELETE)); } diff --git a/test/component/component_test.cpp b/test/component/component_test.cpp index 3aeaba3..6d7f2b4 100644 --- a/test/component/component_test.cpp +++ b/test/component/component_test.cpp @@ -1619,3 +1619,107 @@ TEST_F(ComponentTestInterface, Post100MbsStringView) EXPECT_LE(postPost, prePost + SERVER_RSS_USAGE); } + +/** + * @brief Test the delete request sending a JSON payload. + */ +TEST_F(ComponentTestInterface, DeleteHelloWorldWithPayloadJson) +{ + bool errorOccurred = false; + + HTTPRequest::instance().delete_( + RequestParametersJson {.url = HttpURL("http://localhost:44441/"), .data = R"({"hello":"world"})"_json}, + PostRequestParameters {.onSuccess = + [&](const std::string& result) + { + EXPECT_EQ(result, R"({"hello":"world"})"); + m_callbackComplete = true; + }, + .onError = + [&](const std::string& error, const long, const std::string&) + { + errorOccurred = true; + GTEST_SKIP() << "Test server not available on localhost:44441 - " << error; + }}); + + if (!errorOccurred) + { + EXPECT_TRUE(m_callbackComplete); + } + else + { + SUCCEED(); + } +} + +/** + * @brief Test the delete request sending a raw payload (echo body). + */ +TEST_F(ComponentTestInterface, DeleteHelloWorldWithPayloadRaw) +{ + bool errorOccurred = false; + + HTTPRequest::instance().delete_( + RequestParameters {.url = HttpURL("http://localhost:44441/"), .data = "hello world"}, + PostRequestParameters {.onSuccess = + [&](const std::string& result) + { + EXPECT_EQ(result, "hello world"); + m_callbackComplete = true; + }, + .onError = + [&](const std::string& error, const long, const std::string&) + { + errorOccurred = true; + GTEST_SKIP() << "Test server not available on localhost:44441 - " << error; + }}); + + if (!errorOccurred) + { + EXPECT_TRUE(m_callbackComplete); + } + else + { + SUCCEED(); + } +} + +/** + * @brief Test the DELETE request appending a custom header and sending payload. + */ +TEST_F(ComponentTestInterface, DeleteWithCustomHeaderAndPayload) +{ + const std::string headerKey {"Custom-Key"}; + const std::string headerValue {"Custom-Value"}; + bool errorOccurred = false; + + HTTPRequest::instance().delete_( + RequestParametersJson {.url = HttpURL("http://localhost:44441/check-headers-and-body"), + .data = R"({"hello":"world"})"_json, + .httpHeaders = {headerKey + ":" + headerValue}}, + PostRequestParameters {.onSuccess = + [&](const std::string& result) + { + const auto response = nlohmann::json::parse(result); + + ASSERT_EQ(response.at("headers").at(headerKey), headerValue); + ASSERT_EQ(response.at("body"), R"({"hello":"world"})"); + + m_callbackComplete = true; + }, + .onError = + [&](const std::string& error, const long, const std::string&) + { + errorOccurred = true; + GTEST_SKIP() << "Test server not available on localhost:44441 - " << error; + }}); + + if (!errorOccurred) + { + EXPECT_TRUE(m_callbackComplete); + } + else + { + SUCCEED(); + } +} diff --git a/test/component/component_test.hpp b/test/component/component_test.hpp index 369ffd3..fa7adf5 100644 --- a/test/component/component_test.hpp +++ b/test/component/component_test.hpp @@ -236,6 +236,19 @@ class FakeServer final [&getHttpHeaders](const httplib::Request& req, httplib::Response& res) { res.set_content(getHttpHeaders(req).dump(), "text/json"); }); + server.Delete("/check-headers-and-body", + [&getHttpHeaders](const httplib::Request& req, httplib::Response& res) + { + nlohmann::json out; + out["headers"] = getHttpHeaders(req); + out["body"] = req.body; + res.set_content(out.dump(), "text/json"); + }); + + server.Delete("/", + [](const httplib::Request& req, httplib::Response& res) + { res.set_content(req.body, "text/json"); }); + server.set_keep_alive_max_count(1); server.listen("localhost", 44441); } diff --git a/test/unit/unit_test.cpp b/test/unit/unit_test.cpp index 0590ef7..ea2bd9f 100644 --- a/test/unit/unit_test.cpp +++ b/test/unit/unit_test.cpp @@ -298,6 +298,74 @@ TEST_F(UrlRequestUnitTest, DeleteApiRequest) .execute(); } +/** + * @brief This test checks the API DELETE request with post field. + */ +TEST_F(UrlRequestUnitTest, DeleteApiRequestWithPostFields) +{ + auto request {std::make_shared()}; + + constexpr auto payload = R"({"id":"123"})"; + + EXPECT_CALL(*request, setOptionString(optUrl, "http://www.wazuh.com/")).Times(1); + EXPECT_CALL(*request, setOptionString(optCustomRequest, "DELETE")).Times(1); + EXPECT_CALL(*request, appendHeader("Content-Type: Application/json")).Times(1); + EXPECT_CALL(*request, setOptionString(optUserAgent, "Wazuh-Agent/1.0")).Times(1); + EXPECT_CALL(*request, setOptionString(optCainfo, "cert.ca")).Times(1); + EXPECT_CALL(*request, setOptionLong(optTimeout, 10)).Times(1); + + // Payload + EXPECT_CALL(*request, setOptionString(optPostFields, payload)).Times(1); + EXPECT_CALL(*request, setOptionLong(optPostFieldSize, std::string(payload).length())).Times(1); + + EXPECT_CALL(*request, setOptionLong(optVerifyPeer, 1L)).Times(1); + EXPECT_CALL(*request, execute()).Times(1); + + DeleteRequest::builder(request) + .url("http://www.wazuh.com/") + .appendHeader("Content-Type: Application/json") + .userAgent("Wazuh-Agent/1.0") + .certificate("cert.ca") + .timeout(10) + .template postData(payload) + .execute(); +} + +/** + * @brief This test checks the API DELETE request with post field and unix socket. + */ +TEST_F(UrlRequestUnitTest, DeleteApiRequestWithPostFieldsAndUnixSocket) +{ + auto request {std::make_shared()}; + + constexpr auto payload = R"({"id":"123"})"; + + EXPECT_CALL(*request, setOptionString(optUnixSocketPath, "/tmp/wazuh-agent.sock")).Times(1); + EXPECT_CALL(*request, setOptionString(optUrl, "http://www.wazuh.com/")).Times(1); + EXPECT_CALL(*request, setOptionString(optCustomRequest, "DELETE")).Times(1); + EXPECT_CALL(*request, appendHeader("Content-Type: Application/json")).Times(1); + EXPECT_CALL(*request, setOptionString(optUserAgent, "Wazuh-Agent/1.0")).Times(1); + EXPECT_CALL(*request, setOptionString(optCainfo, "cert.ca")).Times(1); + EXPECT_CALL(*request, setOptionLong(optTimeout, 10)).Times(1); + + // Payload + EXPECT_CALL(*request, setOptionString(optPostFields, payload)).Times(1); + EXPECT_CALL(*request, setOptionLong(optPostFieldSize, std::string(payload).length())).Times(1); + + EXPECT_CALL(*request, setOptionLong(optVerifyPeer, 1L)).Times(1); + EXPECT_CALL(*request, execute()).Times(1); + + DeleteRequest::builder(request) + .url("http://www.wazuh.com/") + .appendHeader("Content-Type: Application/json") + .userAgent("Wazuh-Agent/1.0") + .certificate("cert.ca") + .timeout(10) + .template postData(payload) + .unixSocketPath("/tmp/wazuh-agent.sock") + .execute(); +} + /** * @brief This test checks the malformed API DELETE request. */ diff --git a/test_tool/actions.hpp b/test_tool/actions.hpp index 1a7239c..19d71e5 100644 --- a/test_tool/actions.hpp +++ b/test_tool/actions.hpp @@ -227,7 +227,7 @@ class PutAction final : public IAction void execute() override { HTTPRequest::instance().put( - RequestParameters {.url = HttpURL(m_url), + TRequestParameters {.url = HttpURL(m_url), .data = m_data, .secureCommunication = m_secureCommunication, .httpHeaders = m_headers}, @@ -286,7 +286,7 @@ class PatchAction final : public IAction void execute() override { HTTPRequest::instance().patch( - RequestParameters {.url = HttpURL(m_url), + TRequestParameters {.url = HttpURL(m_url), .data = m_data, .secureCommunication = m_secureCommunication, .httpHeaders = m_headers}, @@ -309,6 +309,7 @@ class DeleteAction final : public IAction { private: std::string m_url; + nlohmann::json m_data; std::unordered_set m_headers; SecureCommunication m_secureCommunication; long m_timeout; @@ -322,10 +323,12 @@ class DeleteAction final : public IAction * @param timeout Timeout for the request. */ explicit DeleteAction(const std::string& url, + const nlohmann::json& data, const std::unordered_set& headers, const SecureCommunication& secureCommunication, const long timeout) : m_url(url) + , m_data(data) , m_headers(headers) , m_secureCommunication(secureCommunication) , m_timeout(timeout) @@ -338,17 +341,21 @@ class DeleteAction final : public IAction void execute() override { HTTPRequest::instance().delete_( - RequestParameters { - .url = HttpURL(m_url), .secureCommunication = m_secureCommunication, .httpHeaders = m_headers}, - PostRequestParameters { + TRequestParameters { + .url = HttpURL(m_url), + .data = m_data, + .secureCommunication = m_secureCommunication, + .httpHeaders = m_headers + }, + PostRequestParameters{ .onSuccess = [](const std::string& msg) { std::cout << msg << std::endl; }, - .onError = - [](const std::string& msg, const long responseCode, const std::string& responseBody) + .onError = [](const std::string& msg, const long code, const std::string& body) { - std::cerr << msg << ": " << responseCode << ". Response body: " << responseBody << std::endl; + std::cerr << msg << ": " << code << ". Response body: " << body << std::endl; throw std::runtime_error(msg); - }}, - ConfigurationParameters {.timeout = m_timeout}); + } + }, + ConfigurationParameters{.timeout = m_timeout}); } }; diff --git a/test_tool/factoryAction.hpp b/test_tool/factoryAction.hpp index 0e5b9c6..c0cfb04 100644 --- a/test_tool/factoryAction.hpp +++ b/test_tool/factoryAction.hpp @@ -78,7 +78,8 @@ class FactoryAction final } else if (0 == args.type().compare("delete")) { - return std::make_unique(args.url(), headers, secureCommunication, timeout); + return std::make_unique( + args.url(), args.postArguments(), headers, secureCommunication, timeout); } else {