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 <abhilash.kollam@gmail.com>
diff --git a/http/http_file_body.hpp b/http/http_file_body.hpp
new file mode 100644
index 0000000..eaafd5d
--- /dev/null
+++ b/http/http_file_body.hpp
@@ -0,0 +1,136 @@
+#pragma once
+
+#include "utility.hpp"
+
+#include <boost/beast/core/file_posix.hpp>
+#include <boost/beast/http/message.hpp>
+#include <boost/system/error_code.hpp>
+
+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<char, readBufSize> fileReadBuf{};
+
+  public:
+    template <bool IsRequest, class Fields>
+    writer(boost::beast::http::header<IsRequest, Fields>& /*header*/,
+           value_type& bodyIn) :
+        body(bodyIn),
+        remain(body.size())
+    {}
+
+    static void init(boost::beast::error_code& ec)
+    {
+        ec = {};
+    }
+
+    boost::optional<std::pair<const_buffers_type, bool>>
+        get(boost::beast::error_code& ec)
+    {
+        size_t toRead = fileReadBuf.size();
+        if (remain < toRead)
+        {
+            toRead = static_cast<size_t>(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<const_buffers_type, bool> 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