From b2896149c39967dd9d1ee79357bdc53537cfabd7 Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Wed, 31 Jan 2024 15:25:47 -0800 Subject: Rename FileBody to HttpBody Now that our custom body type does things more than files, it makes sense to rename it. This commit renames the header itself, then all instances of the class. Tested: Basic GET requests succeed. Change-Id: If4361ac8992fc7c268f48a336707f96e68d3576c Signed-off-by: Ed Tanous --- http/http2_connection.hpp | 4 +- http/http_body.hpp | 283 +++++++++++++++++++++++++++++++++++++++++++++ http/http_client.hpp | 11 +- http/http_connection.hpp | 5 +- http/http_file_body.hpp | 283 --------------------------------------------- http/http_request.hpp | 6 +- http/http_response.hpp | 4 +- http/server_sent_event.hpp | 3 +- http/websocket.hpp | 5 +- 9 files changed, 304 insertions(+), 300 deletions(-) create mode 100644 http/http_body.hpp delete mode 100644 http/http_file_body.hpp (limited to 'http') diff --git a/http/http2_connection.hpp b/http/http2_connection.hpp index 97dcf4e2bb..ed3748059b 100644 --- a/http/http2_connection.hpp +++ b/http/http2_connection.hpp @@ -39,7 +39,7 @@ struct Http2StreamData { Request req{}; Response res{}; - std::optional writer; + std::optional writer; }; template @@ -173,7 +173,7 @@ class HTTP2Connection : } Http2StreamData& stream = it->second; crow::Response& res = stream.res; - http::response& fbody = res.response; + http::response& fbody = res.response; stream.writer.emplace(fbody.base(), fbody.body()); nghttp2_data_provider dataPrd{ diff --git a/http/http_body.hpp b/http/http_body.hpp new file mode 100644 index 0000000000..84351ee303 --- /dev/null +++ b/http/http_body.hpp @@ -0,0 +1,283 @@ +#pragma once + +#include "logging.hpp" +#include "utility.hpp" + +#include + +#include +#include +#include +#include + +#include + +namespace bmcweb +{ +struct HttpBody +{ + class writer; + class reader; + class value_type; +}; + +enum class EncodingType +{ + Raw, + Base64, +}; + +class HttpBody::value_type +{ + boost::beast::file_posix fileHandle; + std::optional fileSize; + std::string strBody; + + public: + EncodingType encodingType = EncodingType::Raw; + + ~value_type() = default; + value_type() = default; + explicit value_type(EncodingType enc) : encodingType(enc) {} + 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) + {} + + 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::string& str() + { + return strBody; + } + + const std::string& str() const + { + return strBody; + } + + std::optional 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); + 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); + } + 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); + + boost::system::error_code ec2; + uint64_t size = fileHandle.size(ec2); + if (!ec2) + { + if (size != 0 && size < std::numeric_limits::max()) + { + fileSize = static_cast(size); + } + } + ec = {}; + } +}; + +class HttpBody::writer +{ + public: + using const_buffers_type = boost::asio::const_buffer; + + private: + std::string buf; + crow::utility::Base64Encoder encoder; + + value_type& body; + size_t sent = 0; + constexpr static size_t readBufSize = 4096; + std::array fileReadBuf{}; + + public: + template + writer(boost::beast::http::header& /*header*/, + value_type& bodyIn) : + body(bodyIn) + {} + + static void init(boost::beast::error_code& ec) + { + ec = {}; + } + + boost::optional> + get(boost::beast::error_code& ec) + { + return getWithMaxSize(ec, std::numeric_limits::max()); + } + + boost::optional> + getWithMaxSize(boost::beast::error_code& ec, size_t maxSize) + { + std::pair ret; + if (!body.file().is_open()) + { + 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 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; + } + + std::string_view chunkView(fileReadBuf.data(), read); + 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(); + 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; + } +}; + +class HttpBody::reader +{ + value_type& value; + + public: + template + reader(boost::beast::http::header& /*headers*/, + value_type& body) : + value(body) + {} + + void init(const boost::optional& contentLength, + boost::beast::error_code& ec) + { + if (contentLength) + { + if (!value.file().is_open()) + { + value.str().reserve(static_cast(*contentLength)); + } + } + ec = {}; + } + + template + 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(b.data()); + value.str() += std::string_view(ptr, b.size()); + } + ec = {}; + return extra; + } + + static void finish(boost::system::error_code& ec) + { + ec = {}; + } +}; + +} // namespace bmcweb diff --git a/http/http_client.hpp b/http/http_client.hpp index 6f42f3ef6e..2d03487e84 100644 --- a/http/http_client.hpp +++ b/http/http_client.hpp @@ -16,6 +16,7 @@ #pragma once #include "async_resolve.hpp" +#include "http_body.hpp" #include "http_response.hpp" #include "logging.hpp" #include "ssl_key_handler.hpp" @@ -116,10 +117,10 @@ struct ConnectionPolicy struct PendingRequest { - boost::beast::http::request req; + boost::beast::http::request req; std::function callback; PendingRequest( - boost::beast::http::request&& reqIn, + boost::beast::http::request&& reqIn, const std::function& callbackIn) : req(std::move(reqIn)), callback(callbackIn) @@ -138,8 +139,8 @@ class ConnectionInfo : public std::enable_shared_from_this uint32_t connId; // Data buffers - http::request req; - using parser_type = http::response_parser; + http::request req; + using parser_type = http::response_parser; std::optional parser; boost::beast::flat_static_buffer buffer; Response res; @@ -733,7 +734,7 @@ class ConnectionPool : public std::enable_shared_from_this const std::function& resHandler) { // Construct the request to be sent - boost::beast::http::request thisReq( + boost::beast::http::request thisReq( verb, destUri.encoded_target(), 11, "", httpHeader); thisReq.set(boost::beast::http::field::host, destUri.encoded_host_address()); diff --git a/http/http_connection.hpp b/http/http_connection.hpp index ed3dc07573..d60e74b9a5 100644 --- a/http/http_connection.hpp +++ b/http/http_connection.hpp @@ -5,6 +5,7 @@ #include "authentication.hpp" #include "complete_response_fields.hpp" #include "http2_connection.hpp" +#include "http_body.hpp" #include "http_response.hpp" #include "http_utility.hpp" #include "logging.hpp" @@ -611,8 +612,8 @@ class Connection : Handler* handler; // Making this a std::optional allows it to be efficiently destroyed and // re-created on Connection reset - std::optional> parser; - std::optional> + std::optional> parser; + std::optional> serializer; boost::beast::flat_static_buffer<8192> buffer; diff --git a/http/http_file_body.hpp b/http/http_file_body.hpp deleted file mode 100644 index 17554a4e9b..0000000000 --- a/http/http_file_body.hpp +++ /dev/null @@ -1,283 +0,0 @@ -#pragma once - -#include "logging.hpp" -#include "utility.hpp" - -#include - -#include -#include -#include -#include - -#include - -namespace bmcweb -{ -struct FileBody -{ - class writer; - class reader; - class value_type; -}; - -enum class EncodingType -{ - Raw, - Base64, -}; - -class FileBody::value_type -{ - boost::beast::file_posix fileHandle; - std::optional fileSize; - std::string strBody; - - public: - EncodingType encodingType = EncodingType::Raw; - - ~value_type() = default; - value_type() = default; - explicit value_type(EncodingType enc) : encodingType(enc) {} - 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) - {} - - 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::string& str() - { - return strBody; - } - - const std::string& str() const - { - return strBody; - } - - std::optional 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); - 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); - } - 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); - - boost::system::error_code ec2; - uint64_t size = fileHandle.size(ec2); - if (!ec2) - { - if (size != 0 && size < std::numeric_limits::max()) - { - fileSize = static_cast(size); - } - } - ec = {}; - } -}; - -class FileBody::writer -{ - public: - using const_buffers_type = boost::asio::const_buffer; - - private: - std::string buf; - crow::utility::Base64Encoder encoder; - - value_type& body; - size_t sent = 0; - constexpr static size_t readBufSize = 4096; - std::array fileReadBuf{}; - - public: - template - writer(boost::beast::http::header& /*header*/, - value_type& bodyIn) : - body(bodyIn) - {} - - static void init(boost::beast::error_code& ec) - { - ec = {}; - } - - boost::optional> - get(boost::beast::error_code& ec) - { - return getWithMaxSize(ec, std::numeric_limits::max()); - } - - boost::optional> - getWithMaxSize(boost::beast::error_code& ec, size_t maxSize) - { - std::pair ret; - if (!body.file().is_open()) - { - 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 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; - } - - std::string_view chunkView(fileReadBuf.data(), read); - 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(); - 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; - } -}; - -class FileBody::reader -{ - value_type& value; - - public: - template - reader(boost::beast::http::header& /*headers*/, - value_type& body) : - value(body) - {} - - void init(const boost::optional& contentLength, - boost::beast::error_code& ec) - { - if (contentLength) - { - if (!value.file().is_open()) - { - value.str().reserve(static_cast(*contentLength)); - } - } - ec = {}; - } - - template - 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(b.data()); - value.str() += std::string_view(ptr, b.size()); - } - ec = {}; - return extra; - } - - static void finish(boost::system::error_code& ec) - { - ec = {}; - } -}; - -} // namespace bmcweb diff --git a/http/http_request.hpp b/http/http_request.hpp index 9cd4a0c656..ee940d658b 100644 --- a/http/http_request.hpp +++ b/http/http_request.hpp @@ -1,6 +1,7 @@ #pragma once #include "common.hpp" +#include "http_body.hpp" #include "sessions.hpp" #include @@ -8,7 +9,6 @@ #include #include #include -#include #include #include @@ -19,7 +19,7 @@ namespace crow struct Request { - boost::beast::http::request req; + boost::beast::http::request req; private: boost::urls::url urlBase{}; @@ -33,7 +33,7 @@ struct Request std::shared_ptr session; std::string userRole{}; - Request(boost::beast::http::request reqIn, + Request(boost::beast::http::request reqIn, std::error_code& ec) : req(std::move(reqIn)) { diff --git a/http/http_response.hpp b/http/http_response.hpp index dca7682a98..afd51c1420 100644 --- a/http/http_response.hpp +++ b/http/http_response.hpp @@ -1,5 +1,5 @@ #pragma once -#include "http_file_body.hpp" +#include "http_body.hpp" #include "logging.hpp" #include "utils/hex_utils.hpp" @@ -23,7 +23,7 @@ struct Response template friend class crow::Connection; - http::response response; + http::response response; nlohmann::json jsonValue; using fields_type = http::header; diff --git a/http/server_sent_event.hpp b/http/server_sent_event.hpp index 730bdce51f..a8bccdb450 100644 --- a/http/server_sent_event.hpp +++ b/http/server_sent_event.hpp @@ -1,4 +1,5 @@ #pragma once +#include "http_body.hpp" #include "http_request.hpp" #include "http_response.hpp" @@ -272,7 +273,7 @@ class ConnectionImpl : public Connection Adaptor adaptor; - using BodyType = bmcweb::FileBody; + using BodyType = bmcweb::HttpBody; boost::beast::http::response res; std::optional> serializer; boost::asio::io_context& ioc; diff --git a/http/websocket.hpp b/http/websocket.hpp index 388a3e4031..d232c02330 100644 --- a/http/websocket.hpp +++ b/http/websocket.hpp @@ -1,5 +1,6 @@ #pragma once #include "async_resp.hpp" +#include "http_body.hpp" #include "http_request.hpp" #include @@ -128,7 +129,7 @@ class ConnectionImpl : public Connection })); // Make a pointer to keep the req alive while we accept it. - using Body = boost::beast::http::request; + using Body = boost::beast::http::request; std::unique_ptr mobile = std::make_unique(req.req); Body* ptr = mobile.get(); // Perform the websocket upgrade @@ -227,7 +228,7 @@ class ConnectionImpl : public Connection void acceptDone(const std::shared_ptr& /*self*/, const std::unique_ptr< - boost::beast::http::request>& /*req*/, + boost::beast::http::request>& /*req*/, const boost::system::error_code& ec) { if (ec) -- cgit v1.2.3