mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 21:34:46 +02:00
Add file_body
This commit is contained in:
@@ -2,6 +2,7 @@ Version 74:
|
||||
|
||||
* Add file_stdio and File concept
|
||||
* Add file_win32
|
||||
* Add file_body
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@@ -79,12 +79,12 @@
|
||||
[def __static_buffer_n__ [link beast.ref.beast__static_buffer_n `static_buffer_n`]]
|
||||
|
||||
[import ../example/common/detect_ssl.hpp]
|
||||
[import ../example/common/file_body.hpp]
|
||||
[import ../example/doc/http_examples.hpp]
|
||||
[import ../example/echo-op/echo_op.cpp]
|
||||
[import ../example/http-client/http_client.cpp]
|
||||
[import ../example/websocket-client/websocket_client.cpp]
|
||||
|
||||
[import ../include/beast/http/file_body.hpp]
|
||||
|
||||
[import ../test/exemplars.cpp]
|
||||
[import ../test/core/doc_snippets.cpp]
|
||||
|
@@ -144,6 +144,14 @@ meet the requirements, or use the ones that come with the library:
|
||||
and parsed; however, body octets received while parsing a message
|
||||
with this body will generate a unique error.
|
||||
]]
|
||||
[[
|
||||
[link beast.ref.beast__http__file_body `file_body`]
|
||||
][
|
||||
This body is represented by a file opened for either reading or
|
||||
writing. Messages with this body may be serialized and parsed.
|
||||
HTTP algorithms will use the open file for reading and writing,
|
||||
for streaming and incremental sends and receives.
|
||||
]]
|
||||
[[
|
||||
[link beast.ref.beast__http__string_body `string_body`]
|
||||
][
|
||||
|
@@ -103,20 +103,22 @@ Use of the flexible __Body__ concept customization point enables authors to
|
||||
preserve the self-contained nature of the __message__ object while allowing
|
||||
domain specific behaviors. Common operations for HTTP servers include sending
|
||||
responses which deliver file contents, and allowing for file uploads. In this
|
||||
example we build the `file_body` type which supports both reading and writing
|
||||
to a file on the file system.
|
||||
example we build the `basic_file_body` type which supports both reading and
|
||||
writing to a file on the file system. The interface is a class templated
|
||||
on the type of file used to access the file system, which must meet the
|
||||
requirements of __File__.
|
||||
|
||||
First we declare the type with its nested types:
|
||||
|
||||
[example_http_file_body_1]
|
||||
|
||||
We will start with the definition of the `value_type`. Our strategy
|
||||
will be to store the open file handle directly in the message
|
||||
container through the `value_type` field. To use this body it will
|
||||
be necessary to call `msg.body.open()` with the file first. This
|
||||
ensures that the file exists throughout the operation and prevent
|
||||
the race condition where the file is removed from the file system
|
||||
in between calls.
|
||||
will be to store the file object directly in the message container
|
||||
through the `value_type` field. To use this body it will be necessary
|
||||
to call `msg.body.file().open()` first with the required information
|
||||
such as the path and open mode. This ensures that the file exists
|
||||
throughout the operation and prevent the race condition where the
|
||||
file is removed from the file system in between calls.
|
||||
|
||||
[example_http_file_body_2]
|
||||
|
||||
@@ -143,9 +145,11 @@ Finally, here is the implementation of the writer member functions:
|
||||
|
||||
We have created a full featured body type capable of reading and
|
||||
writing files on the filesystem, integrating seamlessly with the
|
||||
HTTP algorithms and message container. Source code for this body
|
||||
type, and HTTP servers that use it, are available in the examples
|
||||
directory.
|
||||
HTTP algorithms and message container. The body type works with
|
||||
any file implementation meeting the requirements of __File__ so
|
||||
it may be transparently used with solutions optimized for particular
|
||||
platforms. Example HTTP servers which use file bodies are available
|
||||
in the example directory.
|
||||
|
||||
[endsect]
|
||||
|
||||
|
@@ -30,6 +30,7 @@
|
||||
<bridgehead renderas="sect3">Classes</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
<member><link linkend="beast.ref.beast__http__basic_dynamic_body">basic_dynamic_body</link></member>
|
||||
<member><link linkend="beast.ref.beast__http__basic_file_body">basic_file_body</link></member>
|
||||
<member><link linkend="beast.ref.beast__http__basic_fields">basic_fields</link></member>
|
||||
<member><link linkend="beast.ref.beast__http__basic_parser">basic_parser</link></member>
|
||||
<member><link linkend="beast.ref.beast__http__buffer_body">buffer_body</link></member>
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/field.hpp>
|
||||
#include <beast/http/fields.hpp>
|
||||
#include <beast/http/file_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/parser.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
|
461
include/beast/http/file_body.hpp
Normal file
461
include/beast/http/file_body.hpp
Normal file
@@ -0,0 +1,461 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_FILE_BODY_HPP
|
||||
#define BEAST_HTTP_FILE_BODY_HPP
|
||||
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/file.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
//[example_http_file_body_1
|
||||
|
||||
/** A message body represented by a file on the filesystem.
|
||||
|
||||
Messages with this type have bodies represented by a
|
||||
file on the file system. When parsing a message using
|
||||
this body type, the data is stored in the file pointed
|
||||
to by the path, which must be writable. When serializing,
|
||||
the implementation will read the file and present those
|
||||
octets as the body content. This may be used to serve
|
||||
content from a directory as part of a web service.
|
||||
|
||||
@tparam File The implementation to use for accessing files.
|
||||
This type must meet the requirements of @b File.
|
||||
*/
|
||||
template<class File>
|
||||
struct basic_file_body
|
||||
{
|
||||
static_assert(is_file<File>::value,
|
||||
"File requirements not met");
|
||||
|
||||
/// The type of File this body uses
|
||||
using file_type = File;
|
||||
|
||||
/** Algorithm for retrieving buffers when serializing.
|
||||
|
||||
Objects of this type are created during serialization
|
||||
to extract the buffers representing the body.
|
||||
*/
|
||||
class reader;
|
||||
|
||||
/** Algorithm for storing buffers when parsing.
|
||||
|
||||
Objects of this type are created during parsing
|
||||
to store incoming buffers representing the body.
|
||||
*/
|
||||
class writer;
|
||||
|
||||
/** The type of the @ref message::body member.
|
||||
|
||||
Messages declared using `basic_file_body` will have this
|
||||
type for the body member. This rich class interface
|
||||
allow the file to be opened with the file handle
|
||||
maintained directly in the object, which is attached
|
||||
to the message.
|
||||
*/
|
||||
class value_type;
|
||||
|
||||
/** Returns the size of the body
|
||||
|
||||
@param v The file body to use
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& v);
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_2
|
||||
|
||||
// The body container holds a handle to the file when
|
||||
// it is open, and also caches the size when set.
|
||||
//
|
||||
template<class File>
|
||||
class basic_file_body<File>::value_type
|
||||
{
|
||||
friend class reader;
|
||||
friend class writer;
|
||||
friend struct basic_file_body;
|
||||
|
||||
// This represents the open file
|
||||
File file_;
|
||||
|
||||
// The cached file size
|
||||
std::uint64_t file_size_ = 0;
|
||||
|
||||
public:
|
||||
/** Destructor.
|
||||
|
||||
If the file is open, it is closed first.
|
||||
*/
|
||||
~value_type() = default;
|
||||
|
||||
/// Constructor
|
||||
value_type() = default;
|
||||
|
||||
/// Constructor
|
||||
value_type(value_type&& other) = default;
|
||||
|
||||
/// Move assignment
|
||||
value_type& operator=(value_type&& other) = default;
|
||||
|
||||
/// Returns `true` if the file is open
|
||||
bool
|
||||
is_open() const
|
||||
{
|
||||
return file_.is_open();
|
||||
}
|
||||
|
||||
/// Returns the size of the file if open
|
||||
std::uint64_t
|
||||
size() const
|
||||
{
|
||||
return file_size_;
|
||||
}
|
||||
|
||||
/// Close the file if open
|
||||
void
|
||||
close();
|
||||
|
||||
/** Open a file at the given path with the specified mode
|
||||
|
||||
@param path The utf-8 encoded path to the file
|
||||
|
||||
@param mode The file mode to use
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
void
|
||||
open(char const* path, file_mode mode, error_code& ec);
|
||||
|
||||
/** Set the open file
|
||||
|
||||
This function is used to set the open
|
||||
*/
|
||||
void
|
||||
reset(File&& file, error_code& ec);
|
||||
};
|
||||
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
value_type::
|
||||
close()
|
||||
{
|
||||
error_code ignored;
|
||||
file_.close(ignored);
|
||||
}
|
||||
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
value_type::
|
||||
open(char const* path, file_mode mode, error_code& ec)
|
||||
{
|
||||
// Open the file
|
||||
file_.open(path, mode, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
// Cache the size
|
||||
file_size_ = file_.size(ec);
|
||||
if(ec)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
value_type::
|
||||
reset(File&& file, error_code& ec)
|
||||
{
|
||||
// First close the file if open
|
||||
if(file_.is_open())
|
||||
{
|
||||
error_code ignored;
|
||||
file_.close(ignored);
|
||||
}
|
||||
|
||||
// Take ownership of the new file
|
||||
file_ = std::move(file);
|
||||
|
||||
// Cache the size
|
||||
file_size_ = file_.size(ec);
|
||||
}
|
||||
|
||||
// This is called from message::payload_size
|
||||
template<class File>
|
||||
std::uint64_t
|
||||
basic_file_body<File>::
|
||||
size(value_type const& v)
|
||||
{
|
||||
// Forward the call to the body
|
||||
return v.size();
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_3
|
||||
|
||||
template<class File>
|
||||
class basic_file_body<File>::reader
|
||||
{
|
||||
value_type const& body_; // The body we are reading from
|
||||
std::uint64_t remain_; // The number of unread bytes
|
||||
char buf_[4096]; // Small buffer for reading
|
||||
|
||||
public:
|
||||
// The type of buffer sequence returned by `get`.
|
||||
//
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffers_1;
|
||||
|
||||
// Constructor.
|
||||
//
|
||||
// `m` holds the message we are sending, which will
|
||||
// always have the `file_body` as the body type.
|
||||
//
|
||||
template<bool isRequest, class Fields>
|
||||
reader(
|
||||
message<isRequest, basic_file_body, Fields> const& m,
|
||||
error_code& ec);
|
||||
|
||||
// This function is called zero or more times to
|
||||
// retrieve buffers. A return value of `boost::none`
|
||||
// means there are no more buffers. Otherwise,
|
||||
// the contained pair will have the next buffer
|
||||
// to serialize, and a `bool` indicating whether
|
||||
// or not there may be additional buffers.
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec);
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_4
|
||||
|
||||
// Here we just stash a reference to the path for later.
|
||||
// Rather than dealing with messy constructor exceptions,
|
||||
// we save the things that might fail for the call to `init`.
|
||||
//
|
||||
template<class File>
|
||||
template<bool isRequest, class Fields>
|
||||
basic_file_body<File>::
|
||||
reader::
|
||||
reader(
|
||||
message<isRequest, basic_file_body, Fields> const& m,
|
||||
error_code& ec)
|
||||
: body_(m.body)
|
||||
{
|
||||
// The file must already be open
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
|
||||
// Get the size of the file
|
||||
remain_ = body_.file_.size(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
|
||||
// This function is called repeatedly by the serializer to
|
||||
// retrieve the buffers representing the body. Our strategy
|
||||
// is to read into our buffer and return it until we have
|
||||
// read through the whole file.
|
||||
//
|
||||
template<class File>
|
||||
auto
|
||||
basic_file_body<File>::
|
||||
reader::
|
||||
get(error_code& ec) ->
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
{
|
||||
// Calculate the smaller of our buffer size,
|
||||
// or the amount of unread data in the file.
|
||||
auto const amount = remain_ > sizeof(buf_) ?
|
||||
sizeof(buf_) : static_cast<std::size_t>(remain_);
|
||||
|
||||
// Handle the case where the file is zero length
|
||||
if(amount == 0)
|
||||
{
|
||||
// Modify the error code to indicate success
|
||||
// This is required by the error_code specification.
|
||||
//
|
||||
// NOTE We use the existing category instead of calling
|
||||
// into the library to get the generic category because
|
||||
// that saves us a possibly expensive atomic operation.
|
||||
//
|
||||
ec.assign(0, ec.category());
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Now read the next buffer
|
||||
auto const nread = body_.file_.read(buf_, amount, ec);
|
||||
if(ec)
|
||||
return boost::none;
|
||||
|
||||
// Make sure there is forward progress
|
||||
BOOST_ASSERT(nread != 0);
|
||||
BOOST_ASSERT(nread <= remain_);
|
||||
|
||||
// Update the amount remaining based on what we got
|
||||
remain_ -= nread;
|
||||
|
||||
// Return the buffer to the caller.
|
||||
//
|
||||
// The second element of the pair indicates whether or
|
||||
// not there is more data. As long as there is some
|
||||
// unread bytes, there will be more data. Otherwise,
|
||||
// we set this bool to `false` so we will not be called
|
||||
// again.
|
||||
//
|
||||
ec.assign(0, ec.category());
|
||||
return {{
|
||||
const_buffers_type{buf_, nread}, // buffer to return.
|
||||
remain_ > 0 // `true` if there are more buffers.
|
||||
}};
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_5
|
||||
|
||||
template<class File>
|
||||
class basic_file_body<File>::writer
|
||||
{
|
||||
value_type& body_; // The body we are writing to
|
||||
|
||||
public:
|
||||
// Constructor.
|
||||
//
|
||||
// This is called after the header is parsed and
|
||||
// indicates that a non-zero sized body may be present.
|
||||
// `m` holds the message we are receiving, which will
|
||||
// always have the `file_body` as the body type.
|
||||
//
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(
|
||||
message<isRequest, basic_file_body, Fields>& m,
|
||||
boost::optional<std::uint64_t> const& content_length,
|
||||
error_code& ec);
|
||||
|
||||
// This function is called one or more times to store
|
||||
// buffer sequences corresponding to the incoming body.
|
||||
//
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec);
|
||||
|
||||
// This function is called when writing is complete.
|
||||
// It is an opportunity to perform any final actions
|
||||
// which might fail, in order to return an error code.
|
||||
// Operations that might fail should not be attemped in
|
||||
// destructors, since an exception thrown from there
|
||||
// would terminate the program.
|
||||
//
|
||||
void
|
||||
finish(error_code& ec);
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_6
|
||||
|
||||
// We don't do much in the writer constructor since the
|
||||
// file is already open.
|
||||
//
|
||||
template<class File>
|
||||
template<bool isRequest, class Fields>
|
||||
basic_file_body<File>::
|
||||
writer::
|
||||
writer(
|
||||
message<isRequest, basic_file_body, Fields>& m,
|
||||
boost::optional<std::uint64_t> const& content_length,
|
||||
error_code& ec)
|
||||
: body_(m.body)
|
||||
{
|
||||
// The file must already be open for writing
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
|
||||
// We don't do anything with this but a sophisticated
|
||||
// application might check available space on the device
|
||||
// to see if there is enough room to store the body.
|
||||
boost::ignore_unused(content_length);
|
||||
|
||||
// This is required by the error_code specification
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
// This will get called one or more times with body buffers
|
||||
//
|
||||
template<class File>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_file_body<File>::
|
||||
writer::
|
||||
put(ConstBufferSequence const& buffers, error_code& ec)
|
||||
{
|
||||
// This function must return the total number of
|
||||
// bytes transferred from the input buffers.
|
||||
std::size_t nwritten = 0;
|
||||
|
||||
// Loop over all the buffers in the sequence,
|
||||
// and write each one to the file.
|
||||
for(boost::asio::const_buffer buffer : buffers)
|
||||
{
|
||||
// Write this buffer to the file
|
||||
nwritten += body_.file_.write(
|
||||
boost::asio::buffer_cast<void const*>(buffer),
|
||||
boost::asio::buffer_size(buffer),
|
||||
ec);
|
||||
if(ec)
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
// Indicate success
|
||||
// This is required by the error_code specification
|
||||
ec.assign(0, ec.category());
|
||||
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
// Called after writing is done when there's no error.
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
writer::
|
||||
finish(error_code& ec)
|
||||
{
|
||||
// This has to be cleared before returning, to
|
||||
// indicate no error. The specification requires it.
|
||||
ec.assign(0, ec.category());
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
/// A message body represented by a file on the filesystem.
|
||||
using file_body = basic_file_body<file>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
@@ -22,6 +22,7 @@ add_executable (http-tests
|
||||
error.cpp
|
||||
field.cpp
|
||||
fields.cpp
|
||||
file_body.cpp
|
||||
message.cpp
|
||||
parser.cpp
|
||||
read.cpp
|
||||
|
@@ -15,6 +15,7 @@ unit-test http-tests :
|
||||
error.cpp
|
||||
field.cpp
|
||||
fields.cpp
|
||||
file_body.cpp
|
||||
message.cpp
|
||||
parser.cpp
|
||||
read.cpp
|
||||
|
@@ -6,7 +6,6 @@
|
||||
//
|
||||
|
||||
#include "example/doc/http_examples.hpp"
|
||||
#include "example/common/file_body.hpp"
|
||||
#include "example/common/const_body.hpp"
|
||||
#include "example/common/mutable_body.hpp"
|
||||
|
||||
@@ -286,62 +285,6 @@ public:
|
||||
BEAST_EXPECT(h.body == "Hello, world!");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
doFileBody()
|
||||
{
|
||||
test::pipe c{ios_};
|
||||
|
||||
boost::filesystem::path const path = "temp.txt";
|
||||
std::string const body = "Hello, world!\n";
|
||||
{
|
||||
request<string_body> req;
|
||||
req.version = 11;
|
||||
req.method(verb::put);
|
||||
req.target("/");
|
||||
req.body = body;
|
||||
req.prepare_payload();
|
||||
write(c.client, req);
|
||||
}
|
||||
{
|
||||
flat_buffer b;
|
||||
request_parser<empty_body> p0;
|
||||
read_header(c.server, b, p0);
|
||||
BEAST_EXPECTS(p0.get().method() == verb::put,
|
||||
p0.get().method_string());
|
||||
{
|
||||
error_code ec;
|
||||
request_parser<file_body> p{std::move(p0)};
|
||||
p.get().body.open(path, "wb", ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
read(c.server, b, p);
|
||||
}
|
||||
}
|
||||
{
|
||||
error_code ec;
|
||||
response<file_body> res;
|
||||
res.version = 11;
|
||||
res.result(status::ok);
|
||||
res.insert(field::server, "test");
|
||||
res.body.open(path, "rb", ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
res.set(field::content_length, res.body.size());
|
||||
write(c.server, res);
|
||||
}
|
||||
{
|
||||
flat_buffer b;
|
||||
response<string_body> res;
|
||||
read(c.client, b, res);
|
||||
BEAST_EXPECTS(res.body == body, body);
|
||||
}
|
||||
error_code ec;
|
||||
boost::filesystem::remove(path, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
}
|
||||
|
||||
void
|
||||
doConstAndMutableBody()
|
||||
{
|
||||
@@ -424,7 +367,6 @@ public:
|
||||
doCustomParser();
|
||||
doHEAD();
|
||||
doDeferredBody();
|
||||
doFileBody();
|
||||
doConstAndMutableBody();
|
||||
doIncrementalRead();
|
||||
}
|
||||
|
106
test/http/file_body.cpp
Normal file
106
test/http/file_body.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/file_body.hpp>
|
||||
|
||||
#include <beast/core/file_stdio.hpp>
|
||||
#include <beast/core/flat_buffer.hpp>
|
||||
#include <beast/http/parser.hpp>
|
||||
#include <beast/http/serializer.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class file_body_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
struct lambda
|
||||
{
|
||||
flat_buffer buffer;
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code&, ConstBufferSequence const& buffers)
|
||||
{
|
||||
buffer.commit(boost::asio::buffer_copy(
|
||||
buffer.prepare(boost::asio::buffer_size(buffers)),
|
||||
buffers));
|
||||
}
|
||||
};
|
||||
|
||||
template<class File>
|
||||
void
|
||||
doTestFileBody()
|
||||
{
|
||||
error_code ec;
|
||||
string_view const s =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: test\r\n"
|
||||
"Content-Length: 3\r\n"
|
||||
"\r\n"
|
||||
"xyz";
|
||||
auto const temp = boost::filesystem::unique_path();
|
||||
{
|
||||
response_parser<basic_file_body<File>> p;
|
||||
p.eager(true);
|
||||
|
||||
p.get().body.open(
|
||||
temp.string<std::string>().c_str(), file_mode::write, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
|
||||
p.put(boost::asio::buffer(s.data(), s.size()), ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
}
|
||||
{
|
||||
File f;
|
||||
f.open(temp.string<std::string>().c_str(), file_mode::read, ec);
|
||||
auto size = f.size(ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(size == 3);
|
||||
std::string s1;
|
||||
s1.resize(3);
|
||||
f.read(&s1[0], s1.size(), ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECTS(s1 == "xyz", s);
|
||||
}
|
||||
{
|
||||
lambda visit;
|
||||
{
|
||||
response<basic_file_body<File>> res{status::ok, 11};
|
||||
res.set(field::server, "test");
|
||||
res.body.open(temp.string<std::string>().c_str(),
|
||||
file_mode::scan, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
res.prepare_payload();
|
||||
|
||||
serializer<false, basic_file_body<File>, fields> sr{res};
|
||||
sr.next(ec, visit);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
auto const cb = *visit.buffer.data().begin();
|
||||
string_view const s1{
|
||||
boost::asio::buffer_cast<char const*>(cb),
|
||||
boost::asio::buffer_size(cb)};
|
||||
BEAST_EXPECTS(s1 == s, s1);
|
||||
}
|
||||
}
|
||||
boost::filesystem::remove(temp, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
}
|
||||
void
|
||||
run() override
|
||||
{
|
||||
doTestFileBody<file_stdio>();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(file_body,http,beast);
|
||||
|
||||
} // http
|
||||
} // beast
|
Reference in New Issue
Block a user