From b5f288d294e17719f30e32acc40e07681baf04b9 Mon Sep 17 00:00:00 2001 From: Abhilash Raju Date: Wed, 8 Nov 2023 22:32:44 -0600 Subject: Make use of filebody for dump offload Logservice has been rewritten to use file_body to offload dump files from BMC. There are two kind of dump files, BMC dump and System dump.While BMC dump just requires default support from beast::file_body, System dump requires base64 encoding support from beast. But beast::file_body do not have ready-made support for base64 encoding. So a custom file_body has been written for the base64 encoding. The openFile apis in crow::Response do not have support for unix file descriptor. Since dump files are accesses via descriptors, added new openFile api that accepts descriptors. Tested: Functionality test have been executed to verify the bmc dump offload. Did sanity test by invoking bmcweb pages via browser. Change-Id: I24192657c03d8b2f0394d31e7424c6796ba3227a Signed-off-by: Abhilash Raju --- http/http_file_body.hpp | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ http/http_response.hpp | 34 +++++++++--- 2 files changed, 163 insertions(+), 7 deletions(-) create mode 100644 http/http_file_body.hpp (limited to 'http') diff --git a/http/http_file_body.hpp b/http/http_file_body.hpp new file mode 100644 index 0000000000..eaafd5dbeb --- /dev/null +++ b/http/http_file_body.hpp @@ -0,0 +1,136 @@ +#pragma once + +#include "utility.hpp" + +#include +#include +#include + +namespace bmcweb +{ +struct FileBody +{ + class writer; + class value_type; + + static std::uint64_t size(const value_type& body); +}; + +enum class EncodingType +{ + Raw, + Base64, +}; + +class FileBody::value_type +{ + boost::beast::file_posix fileHandle; + + std::uint64_t fileSize = 0; + + public: + EncodingType encodingType = EncodingType::Raw; + + ~value_type() = default; + value_type() = default; + explicit value_type(EncodingType enc) : encodingType(enc) {} + value_type(value_type&& other) = default; + value_type& operator=(value_type&& other) = default; + value_type(const value_type& other) = delete; + value_type& operator=(const value_type& other) = delete; + + boost::beast::file_posix& file() + { + return fileHandle; + } + + std::uint64_t size() const + { + return fileSize; + } + + void open(const char* path, boost::beast::file_mode mode, + boost::system::error_code& ec) + { + fileHandle.open(path, mode, ec); + fileSize = fileHandle.size(ec); + } + + void setFd(int fd, boost::system::error_code& ec) + { + fileHandle.native_handle(fd); + fileSize = fileHandle.size(ec); + } +}; + +inline std::uint64_t FileBody::size(const value_type& body) +{ + return body.size(); +} + +class FileBody::writer +{ + public: + using const_buffers_type = boost::asio::const_buffer; + + private: + std::string buf; + crow::utility::Base64Encoder encoder; + + value_type& body; + std::uint64_t remain; + constexpr static size_t readBufSize = 4096; + std::array fileReadBuf{}; + + public: + template + writer(boost::beast::http::header& /*header*/, + value_type& bodyIn) : + body(bodyIn), + remain(body.size()) + {} + + static void init(boost::beast::error_code& ec) + { + ec = {}; + } + + boost::optional> + get(boost::beast::error_code& ec) + { + size_t toRead = fileReadBuf.size(); + if (remain < toRead) + { + toRead = static_cast(remain); + } + size_t read = body.file().read(fileReadBuf.data(), toRead, ec); + if (read != toRead || ec) + { + return boost::none; + } + remain -= read; + + std::string_view chunkView(fileReadBuf.data(), read); + + std::pair ret; + ret.second = remain > 0; + if (body.encodingType == EncodingType::Base64) + { + buf.clear(); + buf.reserve( + crow::utility::Base64Encoder::encodedSize(chunkView.size())); + encoder.encode(chunkView, buf); + if (!ret.second) + { + encoder.finalize(buf); + } + ret.first = const_buffers_type(buf.data(), buf.size()); + } + else + { + ret.first = const_buffers_type(chunkView.data(), chunkView.size()); + } + return ret; + } +}; +} // namespace bmcweb diff --git a/http/http_response.hpp b/http/http_response.hpp index ec54a90c4b..40158992e4 100644 --- a/http/http_response.hpp +++ b/http/http_response.hpp @@ -1,8 +1,8 @@ #pragma once +#include "http_file_body.hpp" #include "logging.hpp" #include "utils/hex_utils.hpp" -#include #include #include #include @@ -27,7 +27,7 @@ struct Response friend class crow::Connection; using string_response = http::response; - using file_response = http::response; + using file_response = http::response; // Use boost variant2 because it doesn't have valueless by exception boost::variant2::variant response; @@ -356,25 +356,45 @@ struct Response response); } - bool openFile(const std::filesystem::path& path) + bool openFile(const std::filesystem::path& path, + bmcweb::EncodingType enc = bmcweb::EncodingType::Raw) { - http::file_body::value_type file; + file_response::body_type::value_type body(enc); boost::beast::error_code ec; - file.open(path.c_str(), boost::beast::file_mode::read, ec); + body.open(path.c_str(), boost::beast::file_mode::read, ec); if (ec) { return false; } + updateFileBody(std::move(body)); + return true; + } + + bool openFd(int fd, bmcweb::EncodingType enc = bmcweb::EncodingType::Raw) + { + file_response::body_type::value_type body(enc); + boost::beast::error_code ec; + body.setFd(fd, ec); + if (ec) + { + BMCWEB_LOG_ERROR("Failed to set fd"); + return false; + } + updateFileBody(std::move(body)); + return true; + } + + private: + void updateFileBody(file_response::body_type::value_type file) + { // store the headers on stack temporarily so we can reconstruct the new // base with the old headers copied in. http::header headTemp = std::move(fields()); file_response& fileResponse = response.emplace(std::move(headTemp)); fileResponse.body() = std::move(file); - return true; } - private: std::optional expectedHash; bool completed = false; std::function completeRequestHandler; -- cgit v1.2.3