diff options
Diffstat (limited to 'http/http_file_body.hpp')
-rw-r--r-- | http/http_file_body.hpp | 205 |
1 files changed, 176 insertions, 29 deletions
diff --git a/http/http_file_body.hpp b/http/http_file_body.hpp index eaafd5dbeb..17554a4e9b 100644 --- a/http/http_file_body.hpp +++ b/http/http_file_body.hpp @@ -1,19 +1,24 @@ #pragma once +#include "logging.hpp" #include "utility.hpp" +#include <unistd.h> + +#include <boost/beast/core/buffers_range.hpp> #include <boost/beast/core/file_posix.hpp> #include <boost/beast/http/message.hpp> #include <boost/system/error_code.hpp> +#include <string_view> + namespace bmcweb { struct FileBody { class writer; + class reader; class value_type; - - static std::uint64_t size(const value_type& body); }; enum class EncodingType @@ -25,8 +30,8 @@ enum class EncodingType class FileBody::value_type { boost::beast::file_posix fileHandle; - - std::uint64_t fileSize = 0; + std::optional<size_t> fileSize; + std::string strBody; public: EncodingType encodingType = EncodingType::Raw; @@ -34,40 +39,122 @@ class FileBody::value_type ~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; + explicit value_type(std::string_view str) : strBody(str) {} + + value_type(value_type&& other) noexcept : + fileHandle(std::move(other.fileHandle)), fileSize(other.fileSize), + strBody(std::move(other.strBody)), encodingType(other.encodingType) + {} - boost::beast::file_posix& file() + value_type& operator=(value_type&& other) noexcept + { + fileHandle = std::move(other.fileHandle); + fileSize = other.fileSize; + strBody = std::move(other.strBody); + encodingType = other.encodingType; + + return *this; + } + + // Overload copy constructor, because posix doesn't have dup(), but linux + // does + value_type(const value_type& other) : + fileSize(other.fileSize), strBody(other.strBody), + encodingType(other.encodingType) + { + fileHandle.native_handle(dup(other.fileHandle.native_handle())); + } + + value_type& operator=(const value_type& other) + { + if (this != &other) + { + fileSize = other.fileSize; + strBody = other.strBody; + encodingType = other.encodingType; + fileHandle.native_handle(dup(other.fileHandle.native_handle())); + } + return *this; + } + + const boost::beast::file_posix& file() { return fileHandle; } - std::uint64_t size() const + std::string& str() + { + return strBody; + } + + const std::string& str() const { + return strBody; + } + + std::optional<size_t> payloadSize() const + { + if (!fileHandle.is_open()) + { + return strBody.size(); + } + if (fileSize) + { + if (encodingType == EncodingType::Base64) + { + return crow::utility::Base64Encoder::encodedSize(*fileSize); + } + } return fileSize; } + void clear() + { + strBody.clear(); + strBody.shrink_to_fit(); + fileHandle = boost::beast::file_posix(); + fileSize = std::nullopt; + } + void open(const char* path, boost::beast::file_mode mode, boost::system::error_code& ec) { fileHandle.open(path, mode, ec); - fileSize = fileHandle.size(ec); + if (ec) + { + return; + } + boost::system::error_code ec2; + uint64_t size = fileHandle.size(ec2); + if (!ec2) + { + BMCWEB_LOG_INFO("File size was {} bytes", size); + fileSize = static_cast<size_t>(size); + } + else + { + BMCWEB_LOG_WARNING("Failed to read file size on {}", path); + } + ec = {}; } void setFd(int fd, boost::system::error_code& ec) { fileHandle.native_handle(fd); - fileSize = fileHandle.size(ec); + + boost::system::error_code ec2; + uint64_t size = fileHandle.size(ec2); + if (!ec2) + { + if (size != 0 && size < std::numeric_limits<size_t>::max()) + { + fileSize = static_cast<size_t>(size); + } + } + ec = {}; } }; -inline std::uint64_t FileBody::size(const value_type& body) -{ - return body.size(); -} - class FileBody::writer { public: @@ -78,7 +165,7 @@ class FileBody::writer crow::utility::Base64Encoder encoder; value_type& body; - std::uint64_t remain; + size_t sent = 0; constexpr static size_t readBufSize = 4096; std::array<char, readBufSize> fileReadBuf{}; @@ -86,8 +173,7 @@ class FileBody::writer template <bool IsRequest, class Fields> writer(boost::beast::http::header<IsRequest, Fields>& /*header*/, value_type& bodyIn) : - body(bodyIn), - remain(body.size()) + body(bodyIn) {} static void init(boost::beast::error_code& ec) @@ -98,22 +184,38 @@ class FileBody::writer boost::optional<std::pair<const_buffers_type, bool>> get(boost::beast::error_code& ec) { - size_t toRead = fileReadBuf.size(); - if (remain < toRead) + return getWithMaxSize(ec, std::numeric_limits<size_t>::max()); + } + + boost::optional<std::pair<const_buffers_type, bool>> + getWithMaxSize(boost::beast::error_code& ec, size_t maxSize) + { + std::pair<const_buffers_type, bool> ret; + if (!body.file().is_open()) { - toRead = static_cast<size_t>(remain); + size_t remain = body.str().size() - sent; + size_t toReturn = std::min(maxSize, remain); + ret.first = const_buffers_type(&body.str()[sent], toReturn); + + sent += toReturn; + ret.second = sent < body.str().size(); + BMCWEB_LOG_INFO("Returning {} bytes more={}", ret.first.size(), + ret.second); + return ret; } - size_t read = body.file().read(fileReadBuf.data(), toRead, ec); - if (read != toRead || ec) + size_t readReq = std::min(fileReadBuf.size(), maxSize); + size_t read = body.file().read(fileReadBuf.data(), readReq, ec); + if (ec) { + BMCWEB_LOG_CRITICAL("Failed to read from file"); return boost::none; } - remain -= read; std::string_view chunkView(fileReadBuf.data(), read); - - std::pair<const_buffers_type, bool> ret; - ret.second = remain > 0; + BMCWEB_LOG_INFO("Read {} bytes from file", read); + // If the number of bytes read equals the amount requested, we haven't + // reached EOF yet + ret.second = read == readReq; if (body.encodingType == EncodingType::Base64) { buf.clear(); @@ -133,4 +235,49 @@ class FileBody::writer return ret; } }; + +class FileBody::reader +{ + value_type& value; + + public: + template <bool IsRequest, class Fields> + reader(boost::beast::http::header<IsRequest, Fields>& /*headers*/, + value_type& body) : + value(body) + {} + + void init(const boost::optional<std::uint64_t>& contentLength, + boost::beast::error_code& ec) + { + if (contentLength) + { + if (!value.file().is_open()) + { + value.str().reserve(static_cast<size_t>(*contentLength)); + } + } + ec = {}; + } + + template <class ConstBufferSequence> + std::size_t put(const ConstBufferSequence& buffers, + boost::system::error_code& ec) + { + size_t extra = boost::beast::buffer_bytes(buffers); + for (const auto b : boost::beast::buffers_range_ref(buffers)) + { + const char* ptr = static_cast<const char*>(b.data()); + value.str() += std::string_view(ptr, b.size()); + } + ec = {}; + return extra; + } + + static void finish(boost::system::error_code& ec) + { + ec = {}; + } +}; + } // namespace bmcweb |