2016-05-07 17:06:46 -04:00
|
|
|
//
|
2017-02-06 20:07:03 -05:00
|
|
|
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
2016-05-07 17:06:46 -04:00
|
|
|
//
|
|
|
|
// 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)
|
|
|
|
//
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2017-06-12 19:16:39 -07:00
|
|
|
#ifndef BEAST_EXAMPLE_HTTP_SERVER_FILE_BODY_HPP
|
|
|
|
#define BEAST_EXAMPLE_HTTP_SERVER_FILE_BODY_HPP
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2016-11-10 05:34:49 -05:00
|
|
|
#include <beast/core/error.hpp>
|
|
|
|
#include <beast/http/message.hpp>
|
2017-07-20 08:01:46 -07:00
|
|
|
#include <boost/filesystem.hpp>
|
2017-06-08 22:03:58 -07:00
|
|
|
#include <boost/assert.hpp>
|
2017-05-08 12:41:45 -07:00
|
|
|
#include <boost/optional.hpp>
|
2017-06-08 05:43:57 -07:00
|
|
|
#include <algorithm>
|
2017-07-20 08:01:46 -07:00
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdint>
|
2017-06-08 22:03:58 -07:00
|
|
|
#include <utility>
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2017-06-14 20:26:44 -07:00
|
|
|
//[example_http_file_body_1
|
2017-06-08 22:03:58 -07:00
|
|
|
|
2017-06-12 19:16:39 -07:00
|
|
|
/** 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.
|
|
|
|
*/
|
2017-07-20 08:01:46 -07:00
|
|
|
struct file_body
|
|
|
|
{
|
2017-06-08 22:03:58 -07:00
|
|
|
/** The type of the @ref message::body member.
|
|
|
|
|
|
|
|
Messages declared using `file_body` will have this
|
|
|
|
type for the body member. We use a path indicating
|
|
|
|
the location on the file system for which the data
|
|
|
|
will be read or written.
|
|
|
|
*/
|
|
|
|
using value_type = boost::filesystem::path;
|
|
|
|
|
|
|
|
/** Returns the content length of the body in a message.
|
|
|
|
|
|
|
|
This optional static function returns the size of the
|
|
|
|
body in bytes. It is called from @ref message::size to
|
|
|
|
return the payload size, and from @ref message::prepare
|
|
|
|
to automatically set the Content-Length field. If this
|
|
|
|
function is omitted from a body type, calls to
|
|
|
|
@ref message::prepare will set the chunked transfer
|
|
|
|
encoding.
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2017-06-08 22:03:58 -07:00
|
|
|
@param m The message containing a file body to check.
|
|
|
|
|
|
|
|
@return The size of the file in bytes.
|
|
|
|
*/
|
2017-06-05 21:11:33 -07:00
|
|
|
static
|
|
|
|
std::uint64_t
|
2017-06-15 19:30:43 -07:00
|
|
|
size(value_type const& v);
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
/** 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;
|
|
|
|
};
|
|
|
|
|
|
|
|
//]
|
|
|
|
|
2017-06-14 20:26:44 -07:00
|
|
|
//[example_http_file_body_2
|
2017-06-08 22:03:58 -07:00
|
|
|
|
2017-06-12 19:16:39 -07:00
|
|
|
inline
|
2017-06-08 22:03:58 -07:00
|
|
|
std::uint64_t
|
|
|
|
file_body::
|
2017-06-15 19:30:43 -07:00
|
|
|
size(value_type const& v)
|
2017-06-08 22:03:58 -07:00
|
|
|
{
|
2017-06-15 19:30:43 -07:00
|
|
|
return boost::filesystem::file_size(v);
|
2017-06-08 22:03:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
//]
|
|
|
|
|
2017-06-14 20:26:44 -07:00
|
|
|
//[example_http_file_body_3
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
class file_body::reader
|
|
|
|
{
|
|
|
|
value_type const& path_; // Path of the file
|
|
|
|
FILE* file_ = nullptr; // File handle
|
|
|
|
std::uint64_t remain_ = 0; // The number of unread bytes
|
|
|
|
char buf_[4096]; // Small buffer for reading
|
|
|
|
|
|
|
|
public:
|
|
|
|
// This nested type informs the serializer that it should
|
|
|
|
// wait until after sending the header to initialize the
|
|
|
|
// reader. We set this to true, otherwise opening the file
|
|
|
|
// during `init` could introduce latency which delays
|
|
|
|
// the remote endpoint from receiving the header quickly.
|
|
|
|
//
|
|
|
|
using is_deferred = std::true_type;
|
|
|
|
|
|
|
|
// The type of buffer sequence returned by `get`.
|
|
|
|
//
|
|
|
|
using const_buffers_type =
|
|
|
|
boost::asio::const_buffers_1;
|
|
|
|
|
|
|
|
// Constructor.
|
|
|
|
//
|
|
|
|
// This is called after the header is serialized, because
|
|
|
|
// we declared `is_deferred` to be `std::true_type`.
|
|
|
|
// `m` holds the message we are sending, which will
|
|
|
|
// always have the `file_body` as the body type.
|
|
|
|
//
|
|
|
|
template<bool isRequest, class Fields>
|
2017-06-12 19:16:39 -07:00
|
|
|
reader(beast::http::message<isRequest, file_body, Fields> const& m);
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
// Destructor
|
|
|
|
~reader();
|
|
|
|
|
|
|
|
// This function is called once before serialization
|
|
|
|
// of the body is started.
|
|
|
|
//
|
|
|
|
void
|
2017-06-12 19:16:39 -07:00
|
|
|
init(beast::error_code& ec);
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
// 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>>
|
2017-06-12 19:16:39 -07:00
|
|
|
get(beast::error_code& ec);
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
// This function is called when reading 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
|
2017-06-12 19:16:39 -07:00
|
|
|
finish(beast::error_code& ec);
|
2017-06-08 22:03:58 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
//]
|
|
|
|
|
2017-06-14 20:26:44 -07:00
|
|
|
//[example_http_file_body_4
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
// 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<bool isRequest, class Fields>
|
|
|
|
file_body::reader::
|
2017-06-12 19:16:39 -07:00
|
|
|
reader(beast::http::message<isRequest, file_body, Fields> const& m)
|
2017-06-08 22:03:58 -07:00
|
|
|
: path_(m.body)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// This gets called right after construction, and provides
|
|
|
|
// the opportunity to return an error code. The error code
|
|
|
|
// will be propagated to the serializer and eventually
|
|
|
|
// returned to the caller.
|
|
|
|
//
|
|
|
|
inline
|
|
|
|
void
|
|
|
|
file_body::reader::
|
2017-06-12 19:16:39 -07:00
|
|
|
init(beast::error_code& ec)
|
2017-06-08 22:03:58 -07:00
|
|
|
{
|
|
|
|
// Attempt to open the file for reading
|
|
|
|
file_ = fopen(path_.string().c_str(), "rb");
|
|
|
|
|
|
|
|
if(! file_)
|
2017-06-05 21:11:33 -07:00
|
|
|
{
|
2017-06-08 22:03:58 -07:00
|
|
|
// Convert the old-school `errno` into
|
2017-06-17 08:35:09 -07:00
|
|
|
// an error code using the generic category.
|
|
|
|
ec = beast::error_code{errno, beast::generic_category()};
|
2017-06-08 22:03:58 -07:00
|
|
|
return;
|
2017-06-05 21:11:33 -07:00
|
|
|
}
|
|
|
|
|
2017-06-08 22:03:58 -07:00
|
|
|
// The file was opened successfully, get the size
|
|
|
|
// of the file to know how much we need to read.
|
2017-06-13 07:08:52 -07:00
|
|
|
ec = {};
|
2017-06-08 22:03:58 -07:00
|
|
|
remain_ = boost::filesystem::file_size(path_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
inline
|
|
|
|
auto
|
|
|
|
file_body::reader::
|
2017-06-12 19:16:39 -07:00
|
|
|
get(beast::error_code& ec) ->
|
2017-06-08 22:03:58 -07:00
|
|
|
boost::optional<std::pair<const_buffers_type, bool>>
|
|
|
|
{
|
|
|
|
// Calculate the smaller of our buffer size,
|
|
|
|
// or the amount of unread data in the file.
|
2017-06-17 13:04:01 -07:00
|
|
|
auto const amount = remain_ > sizeof(buf_) ?
|
|
|
|
sizeof(buf_) : static_cast<std::size_t>(remain_);
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
// Check for an empty file
|
|
|
|
if(amount == 0)
|
2017-06-13 07:08:52 -07:00
|
|
|
{
|
|
|
|
ec = {};
|
2017-06-08 22:03:58 -07:00
|
|
|
return boost::none;
|
2017-06-13 07:08:52 -07:00
|
|
|
}
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
// Now read the next buffer
|
|
|
|
auto const nread = fread(buf_, 1, amount, file_);
|
|
|
|
|
|
|
|
// Handle any errors
|
|
|
|
if(ferror(file_))
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2017-06-17 08:35:09 -07:00
|
|
|
// Convert the old-school `errno` into
|
|
|
|
// an error code using the generic category.
|
|
|
|
ec = beast::error_code{errno, beast::generic_category()};
|
2017-06-08 22:03:58 -07:00
|
|
|
return boost::none;
|
|
|
|
}
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2017-06-08 22:03:58 -07:00
|
|
|
// Make sure there is forward progress
|
|
|
|
BOOST_ASSERT(nread != 0);
|
|
|
|
BOOST_ASSERT(nread <= remain_);
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2017-06-08 22:03:58 -07:00
|
|
|
// Update the amount remaining based on what we got
|
|
|
|
remain_ -= nread;
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2017-06-08 22:03:58 -07:00
|
|
|
// 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.
|
|
|
|
//
|
2017-06-13 07:08:52 -07:00
|
|
|
ec = {};
|
2017-06-08 22:03:58 -07:00
|
|
|
return {{
|
|
|
|
const_buffers_type{buf_, nread}, // buffer to return.
|
|
|
|
remain_ > 0 // `true` if there are more buffers.
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called after reading is done when there's no error.
|
|
|
|
inline
|
|
|
|
void
|
|
|
|
file_body::reader::
|
2017-06-12 19:16:39 -07:00
|
|
|
finish(beast::error_code& ec)
|
2017-06-08 22:03:58 -07:00
|
|
|
{
|
2017-06-13 07:08:52 -07:00
|
|
|
// Functions which pass back errors are
|
|
|
|
// responsible for clearing the error code.
|
|
|
|
ec = {};
|
2017-06-08 22:03:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// The destructor is always invoked if construction succeeds.
|
|
|
|
//
|
|
|
|
inline
|
|
|
|
file_body::reader::
|
|
|
|
~reader()
|
|
|
|
{
|
|
|
|
// Just close the file if its open
|
|
|
|
if(file_)
|
|
|
|
fclose(file_);
|
|
|
|
|
|
|
|
// In theory fclose() can fail but how would we handle it?
|
|
|
|
}
|
|
|
|
|
|
|
|
//]
|
|
|
|
|
2017-06-14 20:26:44 -07:00
|
|
|
//[example_http_file_body_5
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
class file_body::writer
|
|
|
|
{
|
|
|
|
value_type const& path_; // A path to the file
|
|
|
|
FILE* file_ = nullptr; // The file handle
|
|
|
|
|
|
|
|
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
|
2017-06-12 19:16:39 -07:00
|
|
|
writer(beast::http::message<isRequest, file_body, Fields>& m);
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
// This function is called once before parsing
|
|
|
|
// of the body is started.
|
|
|
|
//
|
|
|
|
void
|
2017-06-12 19:16:39 -07:00
|
|
|
init(boost::optional<std::uint64_t> const& content_length, beast::error_code& ec);
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
// This function is called one or more times to store
|
|
|
|
// buffer sequences corresponding to the incoming body.
|
|
|
|
//
|
|
|
|
template<class ConstBufferSequence>
|
|
|
|
void
|
2017-06-12 19:16:39 -07:00
|
|
|
put(ConstBufferSequence const& buffers, beast::error_code& ec);
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
// 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
|
2017-06-12 19:16:39 -07:00
|
|
|
finish(beast::error_code& ec);
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
// Destructor.
|
|
|
|
//
|
|
|
|
// Avoid calling anything that might fail here.
|
|
|
|
//
|
|
|
|
~writer();
|
|
|
|
};
|
2017-06-09 14:55:44 -07:00
|
|
|
|
2017-06-08 22:03:58 -07:00
|
|
|
//]
|
|
|
|
|
2017-06-14 20:26:44 -07:00
|
|
|
//[example_http_file_body_6
|
2017-06-08 22:03:58 -07:00
|
|
|
|
|
|
|
// Just stash a reference to the path so we can open the file later.
|
|
|
|
template<bool isRequest, class Fields>
|
|
|
|
file_body::writer::
|
2017-06-12 19:16:39 -07:00
|
|
|
writer(beast::http::message<isRequest, file_body, Fields>& m)
|
2017-06-08 22:03:58 -07:00
|
|
|
: path_(m.body)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// This gets called once when we know there's a body.
|
|
|
|
// If the content_length is set, it lets us know the exact size
|
|
|
|
// of the body. An implementation could use this to optimize its
|
|
|
|
// storage strategy. For example by attempting to reserve space
|
|
|
|
// ahead of time.
|
|
|
|
//
|
|
|
|
inline
|
|
|
|
void
|
|
|
|
file_body::writer::
|
2017-06-12 19:16:39 -07:00
|
|
|
init(boost::optional<std::uint64_t> const& content_length, beast::error_code& ec)
|
2017-06-08 22:03:58 -07:00
|
|
|
{
|
|
|
|
// Attempt to open the file for writing
|
|
|
|
file_ = fopen(path_.string().c_str(), "wb");
|
|
|
|
|
|
|
|
if(! file_)
|
|
|
|
{
|
|
|
|
// Convert the old-school `errno` into
|
2017-06-17 08:35:09 -07:00
|
|
|
// an error code using the generic category.
|
|
|
|
ec = beast::error_code{errno, beast::generic_category()};
|
2017-06-08 22:03:58 -07:00
|
|
|
return;
|
|
|
|
}
|
2017-06-13 07:08:52 -07:00
|
|
|
|
|
|
|
// Indicate success
|
|
|
|
ec = {};
|
2017-06-08 22:03:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// This will get called one or more times with body buffers
|
|
|
|
//
|
|
|
|
template<class ConstBufferSequence>
|
|
|
|
void
|
|
|
|
file_body::writer::
|
2017-06-12 19:16:39 -07:00
|
|
|
put(ConstBufferSequence const& buffers, beast::error_code& ec)
|
2017-06-08 22:03:58 -07:00
|
|
|
{
|
|
|
|
// Loop over all the buffers in the sequence,
|
|
|
|
// and write each one to the file.
|
2017-06-13 11:53:06 -07:00
|
|
|
for(boost::asio::const_buffer buffer : buffers)
|
2017-06-08 22:03:58 -07:00
|
|
|
{
|
|
|
|
// Write this buffer to the file
|
|
|
|
fwrite(
|
|
|
|
boost::asio::buffer_cast<void const*>(buffer), 1,
|
|
|
|
boost::asio::buffer_size(buffer),
|
|
|
|
file_);
|
|
|
|
|
|
|
|
// Handle any errors
|
|
|
|
if(ferror(file_))
|
2017-06-09 14:55:44 -07:00
|
|
|
{
|
2017-06-17 08:35:09 -07:00
|
|
|
// Convert the old-school `errno` into
|
|
|
|
// an error code using the generic category.
|
|
|
|
ec = beast::error_code{errno, beast::generic_category()};
|
2017-06-08 22:03:58 -07:00
|
|
|
return;
|
2017-06-09 14:55:44 -07:00
|
|
|
}
|
2017-06-08 22:03:58 -07:00
|
|
|
}
|
2017-06-13 07:08:52 -07:00
|
|
|
|
|
|
|
// Indicate success
|
|
|
|
ec = {};
|
2017-06-08 22:03:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Called after writing is done when there's no error.
|
|
|
|
inline
|
|
|
|
void
|
|
|
|
file_body::writer::
|
2017-06-12 19:16:39 -07:00
|
|
|
finish(beast::error_code& ec)
|
2017-06-08 22:03:58 -07:00
|
|
|
{
|
2017-06-12 19:16:39 -07:00
|
|
|
// This has to be cleared before returning, to
|
|
|
|
// indicate no error. The specification requires it.
|
2017-06-13 07:08:52 -07:00
|
|
|
ec = {};
|
2017-06-08 22:03:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// The destructor is always invoked if construction succeeds
|
|
|
|
//
|
|
|
|
inline
|
|
|
|
file_body::writer::
|
|
|
|
~writer()
|
|
|
|
{
|
|
|
|
// Just close the file if its open
|
|
|
|
if(file_)
|
|
|
|
fclose(file_);
|
|
|
|
|
|
|
|
// In theory fclose() can fail but how would we handle it?
|
|
|
|
}
|
|
|
|
|
|
|
|
//]
|
2017-07-20 08:01:46 -07:00
|
|
|
|
|
|
|
#endif
|