diff --git a/include/boost/beast/http/basic_file_body.hpp b/include/boost/beast/http/basic_file_body.hpp index 7d04772a..28ecacf9 100644 --- a/include/boost/beast/http/basic_file_body.hpp +++ b/include/boost/beast/http/basic_file_body.hpp @@ -160,6 +160,19 @@ public: */ void reset(File&& file, error_code& ec); + + /** Set the cursor position of the file. + + This function can be used to move the cursor of the file ahead + so that only a part gets read. This file will also adjust the + value_type, in case the file is already part of a body. + + @param offset The offset in bytes from the beginning of the file + + @param ec Set to the error, if any occurred + */ + + void seek(std::uint64_t offset, error_code& ec); }; template @@ -210,8 +223,29 @@ reset(File&& file, error_code& ec) // Cache the size file_size_ = file_.size(ec); + + // Consider the offset + if (!ec) + file_size_ -= file_.pos(ec); } +template +void +basic_file_body:: +value_type:: +seek(std::uint64_t offset, error_code& ec) +{ + file_.seek(offset, ec); + // Cache the size + if (!ec) + file_size_ = file_.size(ec); + + // Consider the offset + if (!ec) + file_size_ -= file_.pos(ec); +} + + // This is called from message::payload_size template std::uint64_t diff --git a/include/boost/beast/http/impl/file_body_win32.hpp b/include/boost/beast/http/impl/file_body_win32.hpp index 8af213d2..44ce64d6 100644 --- a/include/boost/beast/http/impl/file_body_win32.hpp +++ b/include/boost/beast/http/impl/file_body_win32.hpp @@ -94,7 +94,7 @@ struct basic_file_body std::uint64_t size() const { - return size_; + return last_ - first_; } void @@ -105,6 +105,9 @@ struct basic_file_body void reset(file_win32&& file, error_code& ec); + + void + seek(std::uint64_t offset, error_code& ec); }; //-------------------------------------------------------------------------- @@ -284,11 +287,29 @@ reset(file_win32&& file, error_code& ec) close(); return; } - first_ = 0; + + first_ = file_.pos(ec); + if(ec) + { + close(); + return; + } + last_ = size_; } } + +inline +void +basic_file_body:: +value_type:: +seek(std::uint64_t offset, error_code& ec) +{ + first_ = offset; + file_.seek(offset, ec); +} + //------------------------------------------------------------------------------ namespace detail { diff --git a/test/beast/http/file_body.cpp b/test/beast/http/file_body.cpp index 106f3512..ced22145 100644 --- a/test/beast/http/file_body.cpp +++ b/test/beast/http/file_body.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,23 @@ public: } }; + struct string_parser + { + parser p; + std::size_t last_consumption; + template + void + operator()(error_code&, ConstBufferSequence const& buffers) + { + error_code ec; + BEAST_EXPECT(!p.is_done()); + if (!p.is_done()) + last_consumption = p.put(buffers, ec); + + BEAST_EXPECT(!ec); + } + }; + struct temp_file { temp_file(std::ostream& logger) @@ -193,6 +211,87 @@ public: } } + template + void + readPartialFile() + { + + auto temp = temp_file(log); + + error_code ec; + string_view const ten = + "0123456789"; // 40 + // create the temporary file + { + std::ofstream fstemp(temp.path().native()); + std::size_t written = 0; + std::size_t to_write = 2048; + while (written < to_write) + { + auto size = (std::min)(ten.size(), to_write - written); + fstemp << ten.substr(0, size); + BEAST_EXPECT(fstemp.good()); + written += size; + } + fstemp.close(); + } + + // open the file and read the header + { + using file_body_type = basic_file_body; + + typename file_body_type::value_type value; + // opened in write mode so we can truncate later + value.open(temp.path().string().c_str(), file_mode::read, ec); + BEAST_EXPECTS(!ec, ec.message()); + + response> res{status::ok, 11}; + res.set(field::server, "test"); + + if (useReset) + { + File f; + f.open(temp.path().string().c_str(), file_mode::read, ec); + BEAST_EXPECTS(!ec, ec.message()); + f.seek(1005, ec); // so we start at char 5 + BEAST_EXPECTS(!ec, ec.message()); + res.body().reset(std::move(f), ec); + BEAST_EXPECTS(!ec, ec.message()); + } + else + { + res.body().open(temp.path().string().c_str(), file_mode::read, ec); + BEAST_EXPECTS(!ec, ec.message()); + res.body().seek(1005, ec); // so we start at char 5 + BEAST_EXPECTS(!ec, ec.message()); + } + + res.prepare_payload(); + BEAST_EXPECT(res.payload_size().value_or(0) == (2048 - 1005)); + BEAST_EXPECTS(! ec, ec.message()); + + string_parser visit; + serializer, fields> sr{res}; + + while (!sr.is_done()) + { + sr.next(ec, visit); + sr.consume(visit.last_consumption); + } + + BEAST_EXPECT(visit.p.is_header_done()); + BEAST_EXPECT(visit.p.content_length_remaining().value() == 0); + + BEAST_EXPECT(visit.p.is_done()); + + auto data = visit.p.get(); + + BEAST_EXPECT(data.payload_size().value_or(0) == (2048 - 1005)); + BEAST_EXPECT(data.body().size() == (2048 - 1005)); + BEAST_EXPECT(data.body().front() == '5'); + } + } + void run() override { @@ -212,6 +311,17 @@ public: fileBodyUnexpectedEofOnGet(); #endif + readPartialFile(); + readPartialFile(); +#if BOOST_BEAST_USE_POSIX_FILE + readPartialFile(); + readPartialFile(); +#endif +#if BOOST_BEAST_USE_WIN32_FILE + readPartialFile(); + readPartialFile(); +#endif + } };