mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-08-09 00:47:51 +00:00
Added thirdparty: boost library
This commit is contained in:
+161
@@ -0,0 +1,161 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_BASIC_DYNAMIC_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_BASIC_DYNAMIC_BODY_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#include <boost/beast/core/detail/buffer.hpp>
|
||||
#include <boost/beast/core/detail/clamp.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A <em>Body</em> using a <em>DynamicBuffer</em>
|
||||
|
||||
This body uses a <em>DynamicBuffer</em> as a memory-based container
|
||||
for holding message payloads. Messages using this body type
|
||||
may be serialized and parsed.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
struct basic_dynamic_body
|
||||
{
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
|
||||
/** The type of container used for the body
|
||||
|
||||
This determines the type of @ref message::body
|
||||
when this body type is used with a message container.
|
||||
*/
|
||||
using value_type = DynamicBuffer;
|
||||
|
||||
/** Returns the payload size of the body
|
||||
|
||||
When this body is used with @ref message::prepare_payload,
|
||||
the Content-Length will be set to the payload size, and
|
||||
any chunked Transfer-Encoding will be removed.
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& v)
|
||||
{
|
||||
return v.size();
|
||||
}
|
||||
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of <em>BodyReader</em>.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using reader = __implementation_defined__;
|
||||
#else
|
||||
class reader
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(header<isRequest, Fields>&, value_type& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> const&, error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
auto const n = buffer_bytes(buffers);
|
||||
if(beast::detail::sum_exceeds(body_.size(), n, body_.max_size()))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::buffer_overflow);
|
||||
return 0;
|
||||
}
|
||||
auto const mb =
|
||||
beast::detail::dynamic_buffer_prepare(
|
||||
body_, (std::min)(n,
|
||||
body_.max_size() - body_.size()),
|
||||
ec, error::buffer_overflow);
|
||||
if(ec)
|
||||
return 0;
|
||||
auto const bytes_transferred =
|
||||
net::buffer_copy(*mb, buffers);
|
||||
body_.commit(bytes_transferred);
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of <em>BodyWriter</em>.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using writer = __implementation_defined__;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
DynamicBuffer const& body_;
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
typename DynamicBuffer::const_buffers_type;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(header<isRequest, Fields> const&, value_type const& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
return {{body_.data(), false}};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+586
@@ -0,0 +1,586 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_BASIC_FILE_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_BASIC_FILE_BODY_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/file_base.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
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 <em>File</em>.
|
||||
*/
|
||||
template<class File>
|
||||
struct basic_file_body
|
||||
{
|
||||
// Make sure the type meets the requirements
|
||||
static_assert(is_file<File>::value,
|
||||
"File type requirements not met");
|
||||
|
||||
/// The type of File this body uses
|
||||
using file_type = File;
|
||||
|
||||
// Algorithm for storing buffers when parsing.
|
||||
class reader;
|
||||
|
||||
// Algorithm for retrieving buffers when serializing.
|
||||
class writer;
|
||||
|
||||
// The type of the @ref message::body member.
|
||||
class value_type;
|
||||
|
||||
/** Returns the size of the body
|
||||
|
||||
@param body The file body to use
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& body);
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_2
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
template<class File>
|
||||
class basic_file_body<File>::value_type
|
||||
{
|
||||
// This body container holds a handle to the file
|
||||
// when it is open, and also caches the size when set.
|
||||
|
||||
#ifndef BOOST_BEAST_DOXYGEN
|
||||
friend class reader;
|
||||
friend class writer;
|
||||
friend struct basic_file_body;
|
||||
#endif
|
||||
|
||||
// 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;
|
||||
|
||||
/// Return the file
|
||||
File& file()
|
||||
{
|
||||
return file_;
|
||||
}
|
||||
|
||||
/// 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 file. Any previously
|
||||
set file will be closed.
|
||||
|
||||
@param file The file to set. The file must be open or else
|
||||
an error occurs
|
||||
|
||||
@param ec Set to the error, if any occurred
|
||||
*/
|
||||
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<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);
|
||||
|
||||
// Consider the offset
|
||||
if (!ec)
|
||||
file_size_ -= file_.pos(ec);
|
||||
}
|
||||
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
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<class File>
|
||||
std::uint64_t
|
||||
basic_file_body<File>::
|
||||
size(value_type const& body)
|
||||
{
|
||||
// Forward the call to the body
|
||||
return body.size();
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_3
|
||||
|
||||
/** Algorithm for retrieving buffers when serializing.
|
||||
|
||||
Objects of this type are created during serialization
|
||||
to extract the buffers representing the body.
|
||||
*/
|
||||
template<class File>
|
||||
class basic_file_body<File>::writer
|
||||
{
|
||||
value_type& body_; // The body we are reading from
|
||||
std::uint64_t remain_; // The number of unread bytes
|
||||
char buf_[BOOST_BEAST_FILE_BUFFER_SIZE]; // Small buffer for reading
|
||||
|
||||
public:
|
||||
// The type of buffer sequence returned by `get`.
|
||||
//
|
||||
using const_buffers_type =
|
||||
net::const_buffer;
|
||||
|
||||
// Constructor.
|
||||
//
|
||||
// `h` holds the headers of the message we are
|
||||
// serializing, while `b` holds the body.
|
||||
//
|
||||
// Note that the message is passed by non-const reference.
|
||||
// This is intentional, because reading from the file
|
||||
// changes its "current position" which counts makes the
|
||||
// operation logically not-const (although it is bitwise
|
||||
// const).
|
||||
//
|
||||
// The BodyWriter concept allows the writer to choose
|
||||
// whether to take the message by const reference or
|
||||
// non-const reference. Depending on the choice, a
|
||||
// serializer constructed using that body type will
|
||||
// require the same const or non-const reference to
|
||||
// construct.
|
||||
//
|
||||
// Readers which accept const messages usually allow
|
||||
// the same body to be serialized by multiple threads
|
||||
// concurrently, while readers accepting non-const
|
||||
// messages may only be serialized by one thread at
|
||||
// a time.
|
||||
//
|
||||
template<bool isRequest, class Fields>
|
||||
writer(header<isRequest, Fields>& h, value_type& b);
|
||||
|
||||
// Initializer
|
||||
//
|
||||
// This is called before the body is serialized and
|
||||
// gives the writer a chance to do something that might
|
||||
// need to return an error code.
|
||||
//
|
||||
void
|
||||
init(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>::
|
||||
writer::
|
||||
writer(header<isRequest, Fields>& h, value_type& b)
|
||||
: body_(b)
|
||||
{
|
||||
boost::ignore_unused(h);
|
||||
|
||||
// The file must already be open
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
|
||||
// Get the size of the file
|
||||
remain_ = body_.file_size_;
|
||||
}
|
||||
|
||||
// Initializer
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
writer::
|
||||
init(error_code& ec)
|
||||
{
|
||||
// The error_code specification requires that we
|
||||
// either set the error to some value, or set it
|
||||
// to indicate no error.
|
||||
//
|
||||
// We don't do anything fancy so set "no error"
|
||||
ec = {};
|
||||
}
|
||||
|
||||
// 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>::
|
||||
writer::
|
||||
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 = {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Now read the next buffer
|
||||
auto const nread = body_.file_.read(buf_, amount, ec);
|
||||
if(ec)
|
||||
return boost::none;
|
||||
|
||||
if (nread == 0)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::short_read);
|
||||
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 = {};
|
||||
return {{
|
||||
const_buffers_type{buf_, nread}, // buffer to return.
|
||||
remain_ > 0 // `true` if there are more buffers.
|
||||
}};
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_5
|
||||
|
||||
/** Algorithm for storing buffers when parsing.
|
||||
|
||||
Objects of this type are created during parsing
|
||||
to store incoming buffers representing the body.
|
||||
*/
|
||||
template<class File>
|
||||
class basic_file_body<File>::reader
|
||||
{
|
||||
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.
|
||||
// `h` holds the received message headers.
|
||||
// `b` is an instance of `basic_file_body`.
|
||||
//
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(header<isRequest, Fields>&h, value_type& b);
|
||||
|
||||
// Initializer
|
||||
//
|
||||
// This is called before the body is parsed and
|
||||
// gives the reader a chance to do something that might
|
||||
// need to return an error code. It informs us of
|
||||
// the payload size (`content_length`) which we can
|
||||
// optionally use for optimization.
|
||||
//
|
||||
void
|
||||
init(boost::optional<std::uint64_t> const&, 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 attempted 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 reader constructor since the
|
||||
// file is already open.
|
||||
//
|
||||
template<class File>
|
||||
template<bool isRequest, class Fields>
|
||||
basic_file_body<File>::
|
||||
reader::
|
||||
reader(header<isRequest, Fields>& h, value_type& body)
|
||||
: body_(body)
|
||||
{
|
||||
boost::ignore_unused(h);
|
||||
}
|
||||
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
reader::
|
||||
init(
|
||||
boost::optional<std::uint64_t> const& content_length,
|
||||
error_code& ec)
|
||||
{
|
||||
// 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);
|
||||
|
||||
// The error_code specification requires that we
|
||||
// either set the error to some value, or set it
|
||||
// to indicate no error.
|
||||
//
|
||||
// We don't do anything fancy so set "no error"
|
||||
ec = {};
|
||||
}
|
||||
|
||||
// This will get called one or more times with body buffers
|
||||
//
|
||||
template<class File>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_file_body<File>::
|
||||
reader::
|
||||
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(auto it = net::buffer_sequence_begin(buffers);
|
||||
it != net::buffer_sequence_end(buffers); ++it)
|
||||
{
|
||||
// Write this buffer to the file
|
||||
net::const_buffer buffer = *it;
|
||||
nwritten += body_.file_.write(
|
||||
buffer.data(), buffer.size(), ec);
|
||||
if(ec)
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
// Indicate success
|
||||
// This is required by the error_code specification
|
||||
ec = {};
|
||||
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
// Called after writing is done when there's no error.
|
||||
template<class File>
|
||||
void
|
||||
basic_file_body<File>::
|
||||
reader::
|
||||
finish(error_code& ec)
|
||||
{
|
||||
// This has to be cleared before returning, to
|
||||
// indicate no error. The specification requires it.
|
||||
ec = {};
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
// operator<< is not supported for file_body
|
||||
template<bool isRequest, class File, class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream&, message<
|
||||
isRequest, basic_file_body<File>, Fields> const&) = delete;
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+702
@@ -0,0 +1,702 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_BASIC_PARSER_HPP
|
||||
#define BOOST_BEAST_HTTP_BASIC_PARSER_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/http/field.hpp>
|
||||
#include <boost/beast/http/verb.hpp>
|
||||
#include <boost/beast/http/detail/basic_parser.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A parser for decoding HTTP/1 wire format messages.
|
||||
|
||||
This parser is designed to efficiently parse messages in the
|
||||
HTTP/1 wire format. It allocates no memory when input is
|
||||
presented as a single contiguous buffer, and uses minimal
|
||||
state. It will handle chunked encoding and it understands
|
||||
the semantics of the Connection, Content-Length, and Upgrade
|
||||
fields.
|
||||
The parser is optimized for the case where the input buffer
|
||||
sequence consists of a single contiguous buffer. The
|
||||
@ref beast::basic_flat_buffer class is provided, which guarantees
|
||||
that the input sequence of the stream buffer will be represented
|
||||
by exactly one contiguous buffer. To ensure the optimum performance
|
||||
of the parser, use @ref beast::basic_flat_buffer with HTTP algorithms
|
||||
such as @ref read, @ref read_some, @ref async_read, and @ref async_read_some.
|
||||
Alternatively, the caller may use custom techniques to ensure that
|
||||
the structured portion of the HTTP message (header or chunk header)
|
||||
is contained in a linear buffer.
|
||||
|
||||
The interface to the parser uses virtual member functions.
|
||||
To use this class, derive your type from @ref basic_parser. When
|
||||
bytes are presented, the implementation will make a series of zero
|
||||
or more calls to virtual functions, which the derived class must
|
||||
implement.
|
||||
|
||||
Every virtual function must be provided by the derived class,
|
||||
or else a compilation error will be generated. The implementation
|
||||
will make sure that `ec` is clear before each virtual function
|
||||
is invoked. If a virtual function sets an error, it is propagated
|
||||
out of the parser to the caller.
|
||||
|
||||
@tparam isRequest A `bool` indicating whether the parser will be
|
||||
presented with request or response message.
|
||||
|
||||
@note If the parser encounters a field value with obs-fold
|
||||
longer than 4 kilobytes in length, an error is generated.
|
||||
*/
|
||||
template<bool isRequest>
|
||||
class basic_parser
|
||||
: private detail::basic_parser_base
|
||||
{
|
||||
boost::optional<std::uint64_t>
|
||||
body_limit_ =
|
||||
boost::optional<std::uint64_t>(
|
||||
default_body_limit(is_request{})); // max payload body
|
||||
std::uint64_t len_ = 0; // size of chunk or body
|
||||
std::uint64_t len0_ = 0; // content length if known
|
||||
std::unique_ptr<char[]> buf_; // temp storage
|
||||
std::size_t buf_len_ = 0; // size of buf_
|
||||
std::size_t skip_ = 0; // resume search here
|
||||
std::uint32_t header_limit_ = 8192; // max header size
|
||||
unsigned short status_ = 0; // response status
|
||||
state state_ = state::nothing_yet; // initial state
|
||||
unsigned f_ = 0; // flags
|
||||
|
||||
// limit on the size of the stack flat buffer
|
||||
static std::size_t constexpr max_stack_buffer = 8192;
|
||||
|
||||
// Message will be complete after reading header
|
||||
static unsigned constexpr flagSkipBody = 1<< 0;
|
||||
|
||||
// Consume input buffers across semantic boundaries
|
||||
static unsigned constexpr flagEager = 1<< 1;
|
||||
|
||||
// The parser has read at least one byte
|
||||
static unsigned constexpr flagGotSome = 1<< 2;
|
||||
|
||||
// Message semantics indicate a body is expected.
|
||||
// cleared if flagSkipBody set
|
||||
//
|
||||
static unsigned constexpr flagHasBody = 1<< 3;
|
||||
|
||||
static unsigned constexpr flagHTTP11 = 1<< 4;
|
||||
static unsigned constexpr flagNeedEOF = 1<< 5;
|
||||
static unsigned constexpr flagExpectCRLF = 1<< 6;
|
||||
static unsigned constexpr flagConnectionClose = 1<< 7;
|
||||
static unsigned constexpr flagConnectionUpgrade = 1<< 8;
|
||||
static unsigned constexpr flagConnectionKeepAlive = 1<< 9;
|
||||
static unsigned constexpr flagContentLength = 1<< 10;
|
||||
static unsigned constexpr flagChunked = 1<< 11;
|
||||
static unsigned constexpr flagUpgrade = 1<< 12;
|
||||
static unsigned constexpr flagFinalChunk = 1<< 13;
|
||||
|
||||
static constexpr
|
||||
std::uint64_t
|
||||
default_body_limit(std::true_type)
|
||||
{
|
||||
// limit for requests
|
||||
return 1 * 1024 * 1024; // 1MB
|
||||
}
|
||||
|
||||
static constexpr
|
||||
std::uint64_t
|
||||
default_body_limit(std::false_type)
|
||||
{
|
||||
// limit for responses
|
||||
return 8 * 1024 * 1024; // 8MB
|
||||
}
|
||||
|
||||
template<bool OtherIsRequest>
|
||||
friend class basic_parser;
|
||||
|
||||
#ifndef BOOST_BEAST_DOXYGEN
|
||||
friend class basic_parser_test;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
/// Default constructor
|
||||
basic_parser() = default;
|
||||
|
||||
/** Move constructor
|
||||
|
||||
@note
|
||||
|
||||
After the move, the only valid operation on the
|
||||
moved-from object is destruction.
|
||||
*/
|
||||
basic_parser(basic_parser &&) = default;
|
||||
|
||||
/// Move assignment
|
||||
basic_parser& operator=(basic_parser &&) = default;
|
||||
|
||||
public:
|
||||
/// `true` if this parser parses requests, `false` for responses.
|
||||
using is_request =
|
||||
std::integral_constant<bool, isRequest>;
|
||||
|
||||
/// Destructor
|
||||
virtual ~basic_parser() = default;
|
||||
|
||||
/// Copy constructor
|
||||
basic_parser(basic_parser const&) = delete;
|
||||
|
||||
/// Copy assignment
|
||||
basic_parser& operator=(basic_parser const&) = delete;
|
||||
|
||||
/// Returns `true` if the parser has received at least one byte of input.
|
||||
bool
|
||||
got_some() const
|
||||
{
|
||||
return state_ != state::nothing_yet;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is complete.
|
||||
|
||||
The message is complete after the full header is prduced
|
||||
and one of the following is true:
|
||||
|
||||
@li The skip body option was set.
|
||||
|
||||
@li The semantics of the message indicate there is no body.
|
||||
|
||||
@li The semantics of the message indicate a body is expected,
|
||||
and the entire body was parsed.
|
||||
*/
|
||||
bool
|
||||
is_done() const
|
||||
{
|
||||
return state_ == state::complete;
|
||||
}
|
||||
|
||||
/** Returns `true` if a the parser has produced the full header.
|
||||
*/
|
||||
bool
|
||||
is_header_done() const
|
||||
{
|
||||
return state_ > state::fields;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is an upgrade message.
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
upgrade() const
|
||||
{
|
||||
return (f_ & flagConnectionUpgrade) != 0;
|
||||
}
|
||||
|
||||
/** Returns `true` if the last value for Transfer-Encoding is "chunked".
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
chunked() const
|
||||
{
|
||||
return (f_ & flagChunked) != 0;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message has keep-alive connection semantics.
|
||||
|
||||
This function always returns `false` if @ref need_eof would return
|
||||
`false`.
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
keep_alive() const;
|
||||
|
||||
/** Returns the optional value of Content-Length if known.
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
boost::optional<std::uint64_t>
|
||||
content_length() const;
|
||||
|
||||
/** Returns the remaining content length if known
|
||||
|
||||
If the message header specifies a Content-Length,
|
||||
the return value will be the number of bytes remaining
|
||||
in the payload body have not yet been parsed.
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
boost::optional<std::uint64_t>
|
||||
content_length_remaining() const;
|
||||
|
||||
/** Returns `true` if the message semantics require an end of file.
|
||||
|
||||
Depending on the contents of the header, the parser may
|
||||
require and end of file notification to know where the end
|
||||
of the body lies. If this function returns `true` it will be
|
||||
necessary to call @ref put_eof when there will never be additional
|
||||
data from the input.
|
||||
*/
|
||||
bool
|
||||
need_eof() const
|
||||
{
|
||||
return (f_ & flagNeedEOF) != 0;
|
||||
}
|
||||
|
||||
/** Set the limit on the payload body.
|
||||
|
||||
This function sets the maximum allowed size of the payload body,
|
||||
before any encodings except chunked have been removed. Depending
|
||||
on the message semantics, one of these cases will apply:
|
||||
|
||||
@li The Content-Length is specified and exceeds the limit. In
|
||||
this case the result @ref error::body_limit is returned
|
||||
immediately after the header is parsed.
|
||||
|
||||
@li The Content-Length is unspecified and the chunked encoding
|
||||
is not specified as the last encoding. In this case the end of
|
||||
message is determined by the end of file indicator on the
|
||||
associated stream or input source. If a sufficient number of
|
||||
body payload octets are presented to the parser to exceed the
|
||||
configured limit, the parse fails with the result
|
||||
@ref error::body_limit
|
||||
|
||||
@li The Transfer-Encoding specifies the chunked encoding as the
|
||||
last encoding. In this case, when the number of payload body
|
||||
octets produced by removing the chunked encoding exceeds
|
||||
the configured limit, the parse fails with the result
|
||||
@ref error::body_limit.
|
||||
|
||||
Setting the limit after any body octets have been parsed
|
||||
results in undefined behavior.
|
||||
|
||||
The default limit is 1MB for requests and 8MB for responses.
|
||||
|
||||
@param v An optional integral value representing the body limit.
|
||||
If this is equal to `boost::none`, then the body limit is disabled.
|
||||
*/
|
||||
void
|
||||
body_limit(boost::optional<std::uint64_t> v)
|
||||
{
|
||||
body_limit_ = v;
|
||||
}
|
||||
|
||||
/** Set a limit on the total size of the header.
|
||||
|
||||
This function sets the maximum allowed size of the header
|
||||
including all field name, value, and delimiter characters
|
||||
and also including the CRLF sequences in the serialized
|
||||
input. If the end of the header is not found within the
|
||||
limit of the header size, the error @ref error::header_limit
|
||||
is returned by @ref put.
|
||||
|
||||
Setting the limit after any header octets have been parsed
|
||||
results in undefined behavior.
|
||||
*/
|
||||
void
|
||||
header_limit(std::uint32_t v)
|
||||
{
|
||||
header_limit_ = v;
|
||||
}
|
||||
|
||||
/// Returns `true` if the eager parse option is set.
|
||||
bool
|
||||
eager() const
|
||||
{
|
||||
return (f_ & flagEager) != 0;
|
||||
}
|
||||
|
||||
/** Set the eager parse option.
|
||||
|
||||
Normally the parser returns after successfully parsing a structured
|
||||
element (header, chunk header, or chunk body) even if there are octets
|
||||
remaining in the input. This is necessary when attempting to parse the
|
||||
header first, or when the caller wants to inspect information which may
|
||||
be invalidated by subsequent parsing, such as a chunk extension. The
|
||||
`eager` option controls whether the parser keeps going after parsing
|
||||
structured element if there are octets remaining in the buffer and no
|
||||
error occurs. This option is automatically set or cleared during certain
|
||||
stream operations to improve performance with no change in functionality.
|
||||
|
||||
The default setting is `false`.
|
||||
|
||||
@param v `true` to set the eager parse option or `false` to disable it.
|
||||
*/
|
||||
void
|
||||
eager(bool v)
|
||||
{
|
||||
if(v)
|
||||
f_ |= flagEager;
|
||||
else
|
||||
f_ &= ~flagEager;
|
||||
}
|
||||
|
||||
/// Returns `true` if the skip parse option is set.
|
||||
bool
|
||||
skip() const
|
||||
{
|
||||
return (f_ & flagSkipBody) != 0;
|
||||
}
|
||||
|
||||
/** Set the skip parse option.
|
||||
|
||||
This option controls whether or not the parser expects to see an HTTP
|
||||
body, regardless of the presence or absence of certain fields such as
|
||||
Content-Length or a chunked Transfer-Encoding. Depending on the request,
|
||||
some responses do not carry a body. For example, a 200 response to a
|
||||
CONNECT request from a tunneling proxy, or a response to a HEAD request.
|
||||
In these cases, callers may use this function inform the parser that
|
||||
no body is expected. The parser will consider the message complete
|
||||
after the header has been received.
|
||||
|
||||
@param v `true` to set the skip body option or `false` to disable it.
|
||||
|
||||
@note This function must called before any bytes are processed.
|
||||
*/
|
||||
void
|
||||
skip(bool v);
|
||||
|
||||
/** Write a buffer sequence to the parser.
|
||||
|
||||
This function attempts to incrementally parse the HTTP
|
||||
message data stored in the caller provided buffers. Upon
|
||||
success, a positive return value indicates that the parser
|
||||
made forward progress, consuming that number of
|
||||
bytes.
|
||||
|
||||
In some cases there may be an insufficient number of octets
|
||||
in the input buffer in order to make forward progress. This
|
||||
is indicated by the code @ref error::need_more. When
|
||||
this happens, the caller should place additional bytes into
|
||||
the buffer sequence and call @ref put again.
|
||||
|
||||
The error code @ref error::need_more is special. When this
|
||||
error is returned, a subsequent call to @ref put may succeed
|
||||
if the buffers have been updated. Otherwise, upon error
|
||||
the parser may not be restarted.
|
||||
|
||||
@param buffers An object meeting the requirements of
|
||||
<em>ConstBufferSequence</em> that represents the next chunk of
|
||||
message data. If the length of this buffer sequence is
|
||||
one, the implementation will not allocate additional memory.
|
||||
The class @ref beast::basic_flat_buffer is provided as one way to
|
||||
meet this requirement
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of octets consumed in the buffer
|
||||
sequence. The caller should remove these octets even if the
|
||||
error is set.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers, error_code& ec);
|
||||
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
std::size_t
|
||||
put(net::const_buffer buffer,
|
||||
error_code& ec);
|
||||
#endif
|
||||
|
||||
/** Inform the parser that the end of stream was reached.
|
||||
|
||||
In certain cases, HTTP needs to know where the end of
|
||||
the stream is. For example, sometimes servers send
|
||||
responses without Content-Length and expect the client
|
||||
to consume input (for the body) until EOF. Callbacks
|
||||
and errors will still be processed as usual.
|
||||
|
||||
This is typically called when a read from the
|
||||
underlying stream object sets the error code to
|
||||
`net::error::eof`.
|
||||
|
||||
@note Only valid after parsing a complete header.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
void
|
||||
put_eof(error_code& ec);
|
||||
|
||||
protected:
|
||||
/** Called after receiving the request-line.
|
||||
|
||||
This virtual function is invoked after receiving a request-line
|
||||
when parsing HTTP requests.
|
||||
It can only be called when `isRequest == true`.
|
||||
|
||||
@param method The verb enumeration. If the method string is not
|
||||
one of the predefined strings, this value will be @ref verb::unknown.
|
||||
|
||||
@param method_str The unmodified string representing the verb.
|
||||
|
||||
@param target The request-target.
|
||||
|
||||
@param version The HTTP-version. This will be 10 for HTTP/1.0,
|
||||
and 11 for HTTP/1.1.
|
||||
|
||||
@param ec An output parameter which the function may set to indicate
|
||||
an error. The error will be clear before this function is invoked.
|
||||
*/
|
||||
virtual
|
||||
void
|
||||
on_request_impl(
|
||||
verb method,
|
||||
string_view method_str,
|
||||
string_view target,
|
||||
int version,
|
||||
error_code& ec) = 0;
|
||||
|
||||
/** Called after receiving the status-line.
|
||||
|
||||
This virtual function is invoked after receiving a status-line
|
||||
when parsing HTTP responses.
|
||||
It can only be called when `isRequest == false`.
|
||||
|
||||
@param code The numeric status code.
|
||||
|
||||
@param reason The reason-phrase. Note that this value is
|
||||
now obsolete, and only provided for historical or diagnostic
|
||||
purposes.
|
||||
|
||||
@param version The HTTP-version. This will be 10 for HTTP/1.0,
|
||||
and 11 for HTTP/1.1.
|
||||
|
||||
@param ec An output parameter which the function may set to indicate
|
||||
an error. The error will be clear before this function is invoked.
|
||||
*/
|
||||
virtual
|
||||
void
|
||||
on_response_impl(
|
||||
int code,
|
||||
string_view reason,
|
||||
int version,
|
||||
error_code& ec) = 0;
|
||||
|
||||
/** Called once for each complete field in the HTTP header.
|
||||
|
||||
This virtual function is invoked for each field that is received
|
||||
while parsing an HTTP message.
|
||||
|
||||
@param name The known field enum value. If the name of the field
|
||||
is not recognized, this value will be @ref field::unknown.
|
||||
|
||||
@param name_string The exact name of the field as received from
|
||||
the input, represented as a string.
|
||||
|
||||
@param value A string holding the value of the field.
|
||||
|
||||
@param ec An output parameter which the function may set to indicate
|
||||
an error. The error will be clear before this function is invoked.
|
||||
*/
|
||||
virtual
|
||||
void
|
||||
on_field_impl(
|
||||
field name,
|
||||
string_view name_string,
|
||||
string_view value,
|
||||
error_code& ec) = 0;
|
||||
|
||||
/** Called once after the complete HTTP header is received.
|
||||
|
||||
This virtual function is invoked once, after the complete HTTP
|
||||
header is received while parsing a message.
|
||||
|
||||
@param ec An output parameter which the function may set to indicate
|
||||
an error. The error will be clear before this function is invoked.
|
||||
*/
|
||||
virtual
|
||||
void
|
||||
on_header_impl(error_code& ec) = 0;
|
||||
|
||||
/** Called once before the body is processed.
|
||||
|
||||
This virtual function is invoked once, before the content body is
|
||||
processed (but after the complete header is received).
|
||||
|
||||
@param content_length A value representing the content length in
|
||||
bytes if the length is known (this can include a zero length).
|
||||
Otherwise, the value will be `boost::none`.
|
||||
|
||||
@param ec An output parameter which the function may set to indicate
|
||||
an error. The error will be clear before this function is invoked.
|
||||
*/
|
||||
virtual
|
||||
void
|
||||
on_body_init_impl(
|
||||
boost::optional<std::uint64_t> const& content_length,
|
||||
error_code& ec) = 0;
|
||||
|
||||
/** Called each time additional data is received representing the content body.
|
||||
|
||||
This virtual function is invoked for each piece of the body which is
|
||||
received while parsing of a message. This function is only used when
|
||||
no chunked transfer encoding is present.
|
||||
|
||||
@param body A string holding the additional body contents. This may
|
||||
contain nulls or unprintable characters.
|
||||
|
||||
@param ec An output parameter which the function may set to indicate
|
||||
an error. The error will be clear before this function is invoked.
|
||||
|
||||
@see on_chunk_body_impl
|
||||
*/
|
||||
virtual
|
||||
std::size_t
|
||||
on_body_impl(
|
||||
string_view body,
|
||||
error_code& ec) = 0;
|
||||
|
||||
/** Called each time a new chunk header of a chunk encoded body is received.
|
||||
|
||||
This function is invoked each time a new chunk header is received.
|
||||
The function is only used when the chunked transfer encoding is present.
|
||||
|
||||
@param size The size of this chunk, in bytes.
|
||||
|
||||
@param extensions A string containing the entire chunk extensions.
|
||||
This may be empty, indicating no extensions are present.
|
||||
|
||||
@param ec An output parameter which the function may set to indicate
|
||||
an error. The error will be clear before this function is invoked.
|
||||
*/
|
||||
virtual
|
||||
void
|
||||
on_chunk_header_impl(
|
||||
std::uint64_t size,
|
||||
string_view extensions,
|
||||
error_code& ec) = 0;
|
||||
|
||||
/** Called each time additional data is received representing part of a body chunk.
|
||||
|
||||
This virtual function is invoked for each piece of the body which is
|
||||
received while parsing of a message. This function is only used when
|
||||
no chunked transfer encoding is present.
|
||||
|
||||
@param remain The number of bytes remaining in this chunk. This includes
|
||||
the contents of passed `body`. If this value is zero, then this represents
|
||||
the final chunk.
|
||||
|
||||
@param body A string holding the additional body contents. This may
|
||||
contain nulls or unprintable characters.
|
||||
|
||||
@param ec An output parameter which the function may set to indicate
|
||||
an error. The error will be clear before this function is invoked.
|
||||
|
||||
@return This function should return the number of bytes actually consumed
|
||||
from the `body` value. Any bytes that are not consumed on this call
|
||||
will be presented in a subsequent call.
|
||||
|
||||
@see on_body_impl
|
||||
*/
|
||||
virtual
|
||||
std::size_t
|
||||
on_chunk_body_impl(
|
||||
std::uint64_t remain,
|
||||
string_view body,
|
||||
error_code& ec) = 0;
|
||||
|
||||
/** Called once when the complete message is received.
|
||||
|
||||
This virtual function is invoked once, after successfully parsing
|
||||
a complete HTTP message.
|
||||
|
||||
@param ec An output parameter which the function may set to indicate
|
||||
an error. The error will be clear before this function is invoked.
|
||||
*/
|
||||
virtual
|
||||
void
|
||||
on_finish_impl(error_code& ec) = 0;
|
||||
|
||||
private:
|
||||
|
||||
boost::optional<std::uint64_t>
|
||||
content_length_unchecked() const;
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put_from_stack(
|
||||
std::size_t size,
|
||||
ConstBufferSequence const& buffers,
|
||||
error_code& ec);
|
||||
|
||||
void
|
||||
maybe_need_more(
|
||||
char const* p, std::size_t n,
|
||||
error_code& ec);
|
||||
|
||||
void
|
||||
parse_start_line(
|
||||
char const*& p, char const* last,
|
||||
error_code& ec, std::true_type);
|
||||
|
||||
void
|
||||
parse_start_line(
|
||||
char const*& p, char const* last,
|
||||
error_code& ec, std::false_type);
|
||||
|
||||
void
|
||||
parse_fields(
|
||||
char const*& p, char const* last,
|
||||
error_code& ec);
|
||||
|
||||
void
|
||||
finish_header(
|
||||
error_code& ec, std::true_type);
|
||||
|
||||
void
|
||||
finish_header(
|
||||
error_code& ec, std::false_type);
|
||||
|
||||
void
|
||||
parse_body(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_body_to_eof(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_chunk_header(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_chunk_body(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
do_field(field f,
|
||||
string_view value, error_code& ec);
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/basic_parser.hpp>
|
||||
#ifdef BOOST_BEAST_HEADER_ONLY
|
||||
#include <boost/beast/http/impl/basic_parser.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_BUFFER_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_BUFFER_BODY_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A <em>Body</em> using a caller provided buffer
|
||||
|
||||
Messages using this body type may be serialized and parsed.
|
||||
To use this class, the caller must initialize the members
|
||||
of @ref buffer_body::value_type to appropriate values before
|
||||
each call to read or write during a stream operation.
|
||||
*/
|
||||
struct buffer_body
|
||||
{
|
||||
/// The type of the body member when used in a message.
|
||||
struct value_type
|
||||
{
|
||||
/** A pointer to a contiguous area of memory of @ref size octets, else `nullptr`.
|
||||
|
||||
@par When Serializing
|
||||
|
||||
If this is `nullptr` and `more` is `true`, the error
|
||||
@ref error::need_buffer will be returned from @ref serializer::get
|
||||
Otherwise, the serializer will use the memory pointed to
|
||||
by `data` having `size` octets of valid storage as the
|
||||
next buffer representing the body.
|
||||
|
||||
@par When Parsing
|
||||
|
||||
If this is `nullptr`, the error @ref error::need_buffer
|
||||
will be returned from @ref parser::put. Otherwise, the
|
||||
parser will store body octets into the memory pointed to
|
||||
by `data` having `size` octets of valid storage. After
|
||||
octets are stored, the `data` and `size` members are
|
||||
adjusted: `data` is incremented to point to the next
|
||||
octet after the data written, while `size` is decremented
|
||||
to reflect the remaining space at the memory location
|
||||
pointed to by `data`.
|
||||
*/
|
||||
void* data = nullptr;
|
||||
|
||||
/** The number of octets in the buffer pointed to by @ref data.
|
||||
|
||||
@par When Serializing
|
||||
|
||||
If `data` is `nullptr` during serialization, this value
|
||||
is ignored. Otherwise, it represents the number of valid
|
||||
body octets pointed to by `data`.
|
||||
|
||||
@par When Parsing
|
||||
|
||||
The value of this field will be decremented during parsing
|
||||
to indicate the number of remaining free octets in the
|
||||
buffer pointed to by `data`. When it reaches zero, the
|
||||
parser will return @ref error::need_buffer, indicating to
|
||||
the caller that the values of `data` and `size` should be
|
||||
updated to point to a new memory buffer.
|
||||
*/
|
||||
std::size_t size = 0;
|
||||
|
||||
/** `true` if this is not the last buffer.
|
||||
|
||||
@par When Serializing
|
||||
|
||||
If this is `true` and `data` is `nullptr`, the error
|
||||
@ref error::need_buffer will be returned from @ref serializer::get
|
||||
|
||||
@par When Parsing
|
||||
|
||||
This field is not used during parsing.
|
||||
*/
|
||||
bool more = true;
|
||||
};
|
||||
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of <em>BodyReader</em>.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using reader = __implementation_defined__;
|
||||
#else
|
||||
class reader
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(header<isRequest, Fields>&, value_type& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<std::uint64_t> const&, error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
if(! body_.data)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_buffer);
|
||||
return 0;
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
net::buffer_copy(net::buffer(
|
||||
body_.data, body_.size), buffers);
|
||||
body_.data = static_cast<char*>(
|
||||
body_.data) + bytes_transferred;
|
||||
body_.size -= bytes_transferred;
|
||||
if(bytes_transferred == buffer_bytes(buffers))
|
||||
ec = {};
|
||||
else
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_buffer);
|
||||
}
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of <em>BodyWriter</em>.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using writer = __implementation_defined__;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
bool toggle_ = false;
|
||||
value_type const& body_;
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
net::const_buffer;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(header<isRequest, Fields> const&, value_type const& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
|
||||
boost::optional<
|
||||
std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
if(toggle_)
|
||||
{
|
||||
if(body_.more)
|
||||
{
|
||||
toggle_ = false;
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
if(body_.data)
|
||||
{
|
||||
ec = {};
|
||||
toggle_ = true;
|
||||
return {{const_buffers_type{
|
||||
body_.data, body_.size}, body_.more}};
|
||||
}
|
||||
if(body_.more)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_buffer);
|
||||
}
|
||||
else
|
||||
ec = {};
|
||||
return boost::none;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
// operator<< is not supported for buffer_body
|
||||
template<bool isRequest, class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, message<isRequest,
|
||||
buffer_body, Fields> const& msg) = delete;
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+737
@@ -0,0 +1,737 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||
#define BOOST_BEAST_HTTP_CHUNK_ENCODE_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/buffers_cat.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/beast/http/detail/chunk_encode.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A chunked encoding crlf
|
||||
|
||||
This implements a <em>ConstBufferSequence</em> holding the CRLF
|
||||
(`"\r\n"`) used as a delimiter in a @em chunk.
|
||||
|
||||
To use this class, pass an instance of it to a
|
||||
stream algorithm as the buffer sequence:
|
||||
@code
|
||||
// writes "\r\n"
|
||||
net::write(stream, chunk_crlf{});
|
||||
@endcode
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
struct chunk_crlf
|
||||
{
|
||||
/// Constructor
|
||||
chunk_crlf() = default;
|
||||
|
||||
//-----
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using value_type = __implementation_defined__;
|
||||
#else
|
||||
using value_type = net::const_buffer;
|
||||
#endif
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
using const_iterator = value_type const*;
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
chunk_crlf(chunk_crlf const&) = default;
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
static net::const_buffer const cb{"\r\n", 2};
|
||||
return &cb;
|
||||
}
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return begin() + 1;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A @em chunk header
|
||||
|
||||
This implements a <em>ConstBufferSequence</em> representing the
|
||||
header of a @em chunk. The serialized format is as follows:
|
||||
@code
|
||||
chunk-header = 1*HEXDIG chunk-ext CRLF
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
@endcode
|
||||
The chunk extension is optional. After the header and
|
||||
chunk body have been serialized, it is the callers
|
||||
responsibility to also serialize the final CRLF (`"\r\n"`).
|
||||
|
||||
This class allows the caller to emit piecewise chunk bodies,
|
||||
by first serializing the chunk header using this class and then
|
||||
serializing the chunk body in a series of one or more calls to
|
||||
a stream write operation.
|
||||
|
||||
To use this class, pass an instance of it to a
|
||||
stream algorithm as the buffer sequence:
|
||||
@code
|
||||
// writes "400;x\r\n"
|
||||
net::write(stream, chunk_header{1024, "x"});
|
||||
@endcode
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
class chunk_header
|
||||
{
|
||||
using view_type = buffers_cat_view<
|
||||
detail::chunk_size, // chunk-size
|
||||
net::const_buffer, // chunk-extensions
|
||||
chunk_crlf>; // CRLF
|
||||
|
||||
std::shared_ptr<
|
||||
detail::chunk_extensions> exts_;
|
||||
view_type view_;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
This constructs a buffer sequence representing a
|
||||
@em chunked-body size and terminating CRLF (`"\r\n"`)
|
||||
with no chunk extensions.
|
||||
|
||||
@param size The size of the chunk body that follows.
|
||||
The value must be greater than zero.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
explicit
|
||||
chunk_header(std::size_t size);
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs a buffer sequence representing a
|
||||
@em chunked-body size and terminating CRLF (`"\r\n"`)
|
||||
with provided chunk extensions.
|
||||
|
||||
@param size The size of the chunk body that follows.
|
||||
The value must be greater than zero.
|
||||
|
||||
@param extensions The chunk extensions string. This
|
||||
string must be formatted correctly as per rfc7230,
|
||||
using this BNF syntax:
|
||||
@code
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
@endcode
|
||||
The data pointed to by this string view must remain
|
||||
valid for the lifetime of any operations performed on
|
||||
the object.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1.1
|
||||
*/
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
string_view extensions);
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs a buffer sequence representing a
|
||||
@em chunked-body size and terminating CRLF (`"\r\n"`)
|
||||
with provided chunk extensions.
|
||||
The default allocator is used to provide storage for the
|
||||
extensions object.
|
||||
|
||||
@param size The size of the chunk body that follows.
|
||||
The value must be greater than zero.
|
||||
|
||||
@param extensions The chunk extensions object. The expression
|
||||
`extensions.str()` must be valid, and the return type must
|
||||
be convertible to @ref string_view. This object will be copied
|
||||
or moved as needed to ensure that the chunk header object retains
|
||||
ownership of the buffers provided by the chunk extensions object.
|
||||
|
||||
@note This function participates in overload resolution only
|
||||
if @b ChunkExtensions meets the requirements stated above.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
template<class ChunkExtensions
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
, class = typename std::enable_if<
|
||||
detail::is_chunk_extensions<
|
||||
ChunkExtensions>::value>::type
|
||||
#endif
|
||||
>
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
ChunkExtensions&& extensions);
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs a buffer sequence representing a
|
||||
@em chunked-body size and terminating CRLF (`"\r\n"`)
|
||||
with provided chunk extensions.
|
||||
The specified allocator is used to provide storage for the
|
||||
extensions object.
|
||||
|
||||
@param size The size of the chunk body that follows.
|
||||
The value be greater than zero.
|
||||
|
||||
@param extensions The chunk extensions object. The expression
|
||||
`extensions.str()` must be valid, and the return type must
|
||||
be convertible to @ref string_view. This object will be copied
|
||||
or moved as needed to ensure that the chunk header object retains
|
||||
ownership of the buffers provided by the chunk extensions object.
|
||||
|
||||
@param allocator The allocator to provide storage for the moved
|
||||
or copied extensions object.
|
||||
|
||||
@note This function participates in overload resolution only
|
||||
if @b ChunkExtensions meets the requirements stated above.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
template<class ChunkExtensions, class Allocator
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
, class = typename std::enable_if<
|
||||
detail::is_chunk_extensions<
|
||||
ChunkExtensions>::value>::type
|
||||
#endif
|
||||
>
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
ChunkExtensions&& extensions,
|
||||
Allocator const& allocator);
|
||||
|
||||
//-----
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using value_type = __implementation_defined__;
|
||||
#else
|
||||
using value_type = typename view_type::value_type;
|
||||
#endif
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = __implementation_defined__;
|
||||
#else
|
||||
using const_iterator = typename view_type::const_iterator;
|
||||
#endif
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
chunk_header(chunk_header const&) = default;
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return view_.begin();
|
||||
}
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return view_.end();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A @em chunk
|
||||
|
||||
This implements a <em>ConstBufferSequence</em> representing
|
||||
a @em chunk. The serialized format is as follows:
|
||||
@code
|
||||
chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
|
||||
chunk-size = 1*HEXDIG
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
chunk-data = 1*OCTET ; a sequence of chunk-size octets
|
||||
@endcode
|
||||
The chunk extension is optional.
|
||||
|
||||
To use this class, pass an instance of it to a
|
||||
stream algorithm as the buffer sequence.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
class chunk_body
|
||||
{
|
||||
using view_type = buffers_cat_view<
|
||||
detail::chunk_size, // chunk-size
|
||||
net::const_buffer, // chunk-extensions
|
||||
chunk_crlf, // CRLF
|
||||
ConstBufferSequence, // chunk-body
|
||||
chunk_crlf>; // CRLF
|
||||
|
||||
std::shared_ptr<
|
||||
detail::chunk_extensions> exts_;
|
||||
view_type view_;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
This constructs buffers representing a complete @em chunk
|
||||
with no chunk extensions and having the size and contents
|
||||
of the specified buffer sequence.
|
||||
|
||||
@param buffers A buffer sequence representing the chunk
|
||||
body. Although the buffers object may be copied as necessary,
|
||||
ownership of the underlying memory blocks is retained by the
|
||||
caller, which must guarantee that they remain valid while this
|
||||
object is in use.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
explicit
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers);
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs buffers representing a complete @em chunk
|
||||
with the passed chunk extensions and having the size and
|
||||
contents of the specified buffer sequence.
|
||||
|
||||
@param buffers A buffer sequence representing the chunk
|
||||
body. Although the buffers object may be copied as necessary,
|
||||
ownership of the underlying memory blocks is retained by the
|
||||
caller, which must guarantee that they remain valid while this
|
||||
object is in use.
|
||||
|
||||
@param extensions The chunk extensions string. This
|
||||
string must be formatted correctly as per rfc7230,
|
||||
using this BNF syntax:
|
||||
@code
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
@endcode
|
||||
The data pointed to by this string view must remain
|
||||
valid for the lifetime of any operations performed on
|
||||
the object.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1.1
|
||||
*/
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
string_view extensions);
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs buffers representing a complete @em chunk
|
||||
with the passed chunk extensions and having the size and
|
||||
contents of the specified buffer sequence.
|
||||
The default allocator is used to provide storage for the
|
||||
extensions object.
|
||||
|
||||
@param buffers A buffer sequence representing the chunk
|
||||
body. Although the buffers object may be copied as necessary,
|
||||
ownership of the underlying memory blocks is retained by the
|
||||
caller, which must guarantee that they remain valid while this
|
||||
object is in use.
|
||||
|
||||
@param extensions The chunk extensions object. The expression
|
||||
`extensions.str()` must be valid, and the return type must
|
||||
be convertible to @ref string_view. This object will be copied
|
||||
or moved as needed to ensure that the chunk header object retains
|
||||
ownership of the buffers provided by the chunk extensions object.
|
||||
|
||||
@note This function participates in overload resolution only
|
||||
if @b ChunkExtensions meets the requirements stated above.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
template<class ChunkExtensions
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
, class = typename std::enable_if<
|
||||
! std::is_convertible<typename std::decay<
|
||||
ChunkExtensions>::type, string_view>::value>::type
|
||||
#endif
|
||||
>
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
ChunkExtensions&& extensions);
|
||||
|
||||
/** Constructor
|
||||
|
||||
This constructs buffers representing a complete @em chunk
|
||||
with the passed chunk extensions and having the size and
|
||||
contents of the specified buffer sequence.
|
||||
The specified allocator is used to provide storage for the
|
||||
extensions object.
|
||||
|
||||
@param buffers A buffer sequence representing the chunk
|
||||
body. Although the buffers object may be copied as necessary,
|
||||
ownership of the underlying memory blocks is retained by the
|
||||
caller, which must guarantee that they remain valid while this
|
||||
object is in use.
|
||||
|
||||
@param extensions The chunk extensions object. The expression
|
||||
`extensions.str()` must be valid, and the return type must
|
||||
be convertible to @ref string_view. This object will be copied
|
||||
or moved as needed to ensure that the chunk header object retains
|
||||
ownership of the buffers provided by the chunk extensions object.
|
||||
|
||||
@param allocator The allocator to provide storage for the moved
|
||||
or copied extensions object.
|
||||
|
||||
@note This function participates in overload resolution only
|
||||
if @b ChunkExtensions meets the requirements stated above.
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7230#section-4.1
|
||||
*/
|
||||
template<class ChunkExtensions, class Allocator
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
, class = typename std::enable_if<
|
||||
! std::is_convertible<typename std::decay<
|
||||
ChunkExtensions>::type, string_view>::value>::type
|
||||
#endif
|
||||
>
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
ChunkExtensions&& extensions,
|
||||
Allocator const& allocator);
|
||||
|
||||
//-----
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using value_type = __implementation_defined__;
|
||||
#else
|
||||
using value_type = typename view_type::value_type;
|
||||
#endif
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = __implementation_defined__;
|
||||
#else
|
||||
using const_iterator = typename view_type::const_iterator;
|
||||
#endif
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return view_.begin();
|
||||
}
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return view_.end();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A chunked-encoding last chunk
|
||||
*/
|
||||
template<class Trailer = chunk_crlf>
|
||||
class chunk_last
|
||||
{
|
||||
static_assert(
|
||||
is_fields<Trailer>::value ||
|
||||
net::is_const_buffer_sequence<Trailer>::value,
|
||||
"Trailer requirements not met");
|
||||
|
||||
using buffers_type = typename
|
||||
detail::buffers_or_fields<Trailer>::type;
|
||||
|
||||
using view_type =
|
||||
buffers_cat_view<
|
||||
detail::chunk_size0, // "0\r\n"
|
||||
buffers_type>; // Trailer (includes CRLF)
|
||||
|
||||
template<class Allocator>
|
||||
buffers_type
|
||||
prepare(Trailer const& trailer, Allocator const& alloc);
|
||||
|
||||
buffers_type
|
||||
prepare(Trailer const& trailer, std::true_type);
|
||||
|
||||
buffers_type
|
||||
prepare(Trailer const& trailer, std::false_type);
|
||||
|
||||
std::shared_ptr<void> sp_;
|
||||
view_type view_;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
The last chunk will have an empty trailer
|
||||
*/
|
||||
chunk_last();
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param trailer The trailer to use. This may be
|
||||
a type meeting the requirements of either Fields
|
||||
or ConstBufferSequence. If it is a ConstBufferSequence,
|
||||
the trailer must be formatted correctly as per rfc7230
|
||||
including a CRLF on its own line to denote the end
|
||||
of the trailer.
|
||||
*/
|
||||
explicit
|
||||
chunk_last(Trailer const& trailer);
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param trailer The trailer to use. This type must
|
||||
meet the requirements of Fields.
|
||||
|
||||
@param allocator The allocator to use for storing temporary
|
||||
data associated with the serialized trailer buffers.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class Allocator>
|
||||
chunk_last(Trailer const& trailer, Allocator const& allocator);
|
||||
#else
|
||||
template<class DeducedTrailer, class Allocator,
|
||||
class = typename std::enable_if<
|
||||
is_fields<DeducedTrailer>::value>::type>
|
||||
chunk_last(
|
||||
DeducedTrailer const& trailer, Allocator const& allocator);
|
||||
#endif
|
||||
|
||||
//-----
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
chunk_last(chunk_last const&) = default;
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using value_type = __implementation_defined__;
|
||||
#else
|
||||
using value_type =
|
||||
typename view_type::value_type;
|
||||
#endif
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = __implementation_defined__;
|
||||
#else
|
||||
using const_iterator =
|
||||
typename view_type::const_iterator;
|
||||
#endif
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return view_.begin();
|
||||
}
|
||||
|
||||
/// Required for <em>ConstBufferSequence</em>
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return view_.end();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A set of chunk extensions
|
||||
|
||||
This container stores a set of chunk extensions suited for use with
|
||||
@ref chunk_header and @ref chunk_body. The container may be iterated
|
||||
to access the extensions in their structured form.
|
||||
|
||||
Meets the requirements of ChunkExtensions
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_chunk_extensions
|
||||
{
|
||||
std::basic_string<char,
|
||||
std::char_traits<char>, Allocator> s_;
|
||||
|
||||
std::basic_string<char,
|
||||
std::char_traits<char>, Allocator> range_;
|
||||
|
||||
template<class FwdIt>
|
||||
FwdIt
|
||||
do_parse(FwdIt it, FwdIt last, error_code& ec);
|
||||
|
||||
void
|
||||
do_insert(string_view name, string_view value);
|
||||
|
||||
public:
|
||||
/** The type of value when iterating.
|
||||
|
||||
The first element of the pair is the name, and the second
|
||||
element is the value which may be empty. The value is
|
||||
stored in its raw representation, without quotes or escapes.
|
||||
*/
|
||||
using value_type = std::pair<string_view, string_view>;
|
||||
|
||||
class const_iterator;
|
||||
|
||||
/// Constructor
|
||||
basic_chunk_extensions() = default;
|
||||
|
||||
/// Constructor
|
||||
basic_chunk_extensions(basic_chunk_extensions&&) = default;
|
||||
|
||||
/// Constructor
|
||||
basic_chunk_extensions(basic_chunk_extensions const&) = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param allocator The allocator to use for storing the serialized extension
|
||||
*/
|
||||
explicit
|
||||
basic_chunk_extensions(Allocator const& allocator)
|
||||
: s_(allocator)
|
||||
{
|
||||
}
|
||||
|
||||
/** Clear the chunk extensions
|
||||
|
||||
This preserves the capacity of the internal string
|
||||
used to hold the serialized representation.
|
||||
*/
|
||||
void
|
||||
clear()
|
||||
{
|
||||
s_.clear();
|
||||
}
|
||||
|
||||
/** Parse a set of chunk extensions
|
||||
|
||||
Any previous extensions will be cleared
|
||||
*/
|
||||
void
|
||||
parse(string_view s, error_code& ec);
|
||||
|
||||
/** Insert an extension name with an empty value
|
||||
|
||||
@param name The name of the extension
|
||||
*/
|
||||
void
|
||||
insert(string_view name);
|
||||
|
||||
/** Insert an extension value
|
||||
|
||||
@param name The name of the extension
|
||||
|
||||
@param value The value to insert. Depending on the
|
||||
contents, the serialized extension may use a quoted string.
|
||||
*/
|
||||
void
|
||||
insert(string_view name, string_view value);
|
||||
|
||||
/// Return the serialized representation of the chunk extension
|
||||
string_view
|
||||
str() const
|
||||
{
|
||||
return s_;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const;
|
||||
|
||||
const_iterator
|
||||
end() const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// A set of chunk extensions
|
||||
using chunk_extensions =
|
||||
basic_chunk_extensions<std::allocator<char>>;
|
||||
|
||||
/** Returns a @ref chunk_body
|
||||
|
||||
This functions constructs and returns a complete
|
||||
@ref chunk_body for a chunk body represented by the
|
||||
specified buffer sequence.
|
||||
|
||||
@param buffers The buffers representing the chunk body.
|
||||
|
||||
@param args Optional arguments passed to the @ref chunk_body constructor.
|
||||
|
||||
@note This function is provided as a notational convenience
|
||||
to omit specification of the class template arguments.
|
||||
*/
|
||||
template<class ConstBufferSequence, class... Args>
|
||||
auto
|
||||
make_chunk(
|
||||
ConstBufferSequence const& buffers,
|
||||
Args&&... args) ->
|
||||
chunk_body<ConstBufferSequence>
|
||||
{
|
||||
return chunk_body<ConstBufferSequence>(
|
||||
buffers, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/** Returns a @ref chunk_last
|
||||
|
||||
@note This function is provided as a notational convenience
|
||||
to omit specification of the class template arguments.
|
||||
*/
|
||||
inline
|
||||
chunk_last<chunk_crlf>
|
||||
make_chunk_last()
|
||||
{
|
||||
return chunk_last<chunk_crlf>{};
|
||||
}
|
||||
|
||||
/** Returns a @ref chunk_last
|
||||
|
||||
This function construct and returns a complete
|
||||
@ref chunk_last for a last chunk containing the
|
||||
specified trailers.
|
||||
|
||||
@param trailer A ConstBufferSequence or
|
||||
@note This function is provided as a notational convenience
|
||||
to omit specification of the class template arguments.
|
||||
|
||||
@param args Optional arguments passed to the @ref chunk_last
|
||||
constructor.
|
||||
*/
|
||||
template<class Trailer, class... Args>
|
||||
chunk_last<Trailer>
|
||||
make_chunk_last(
|
||||
Trailer const& trailer,
|
||||
Args&&... args)
|
||||
{
|
||||
return chunk_last<Trailer>{
|
||||
trailer, std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/chunk_encode.hpp>
|
||||
|
||||
#endif
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DETAIL_BASIC_PARSED_LIST_HPP
|
||||
#define BOOST_BEAST_HTTP_DETAIL_BASIC_PARSED_LIST_HPP
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/core/empty_value.hpp>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
/** A list parser which presents the sequence as a container.
|
||||
*/
|
||||
template<class Policy>
|
||||
class basic_parsed_list
|
||||
{
|
||||
string_view s_;
|
||||
|
||||
public:
|
||||
/// The type of policy this list uses for parsing.
|
||||
using policy_type = Policy;
|
||||
|
||||
/// The type of each element in the list.
|
||||
using value_type = typename Policy::value_type;
|
||||
|
||||
/// A constant iterator to a list element.
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = __implementation_defined__;
|
||||
#else
|
||||
class const_iterator;
|
||||
#endif
|
||||
|
||||
class const_iterator
|
||||
: private boost::empty_value<Policy>
|
||||
{
|
||||
basic_parsed_list const* list_ = nullptr;
|
||||
char const* it_ = nullptr;
|
||||
typename Policy::value_type v_;
|
||||
bool error_ = false;
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename Policy::value_type;
|
||||
using reference = value_type const&;
|
||||
using pointer = value_type const*;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::forward_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
|
||||
bool
|
||||
operator==(
|
||||
const_iterator const& other) const
|
||||
{
|
||||
return
|
||||
other.list_ == list_ &&
|
||||
other.it_ == it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(
|
||||
const_iterator const& other) const
|
||||
{
|
||||
return ! (*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
bool
|
||||
error() const
|
||||
{
|
||||
return error_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parsed_list;
|
||||
|
||||
const_iterator(
|
||||
basic_parsed_list const& list, bool at_end)
|
||||
: list_(&list)
|
||||
, it_(at_end ? nullptr :
|
||||
list.s_.data())
|
||||
{
|
||||
if(! at_end)
|
||||
increment();
|
||||
}
|
||||
|
||||
void
|
||||
increment()
|
||||
{
|
||||
if(! this->get()(
|
||||
v_, it_, list_->s_))
|
||||
{
|
||||
it_ = nullptr;
|
||||
error_ = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Construct a list from a string
|
||||
explicit
|
||||
basic_parsed_list(string_view s)
|
||||
: s_(s)
|
||||
{
|
||||
}
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator begin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator end() const;
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator cbegin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator cend() const;
|
||||
};
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, false};
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, true};
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, false};
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, true};
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
|
||||
#define BOOST_BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/detail/char_buffer.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/detail/rfc7230.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/version.hpp>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
struct basic_parser_base
|
||||
{
|
||||
// limit on the size of the obs-fold buffer
|
||||
//
|
||||
// https://stackoverflow.com/questions/686217/maximum-on-http-header-values
|
||||
//
|
||||
static std::size_t constexpr max_obs_fold = 4096;
|
||||
|
||||
enum class state
|
||||
{
|
||||
nothing_yet = 0,
|
||||
start_line,
|
||||
fields,
|
||||
body0,
|
||||
body,
|
||||
body_to_eof0,
|
||||
body_to_eof,
|
||||
chunk_header0,
|
||||
chunk_header,
|
||||
chunk_body,
|
||||
complete
|
||||
};
|
||||
|
||||
static
|
||||
bool
|
||||
is_digit(char c)
|
||||
{
|
||||
return static_cast<unsigned char>(c-'0') < 10;
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
is_print(char c)
|
||||
{
|
||||
return static_cast<unsigned char>(c-32) < 95;
|
||||
}
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
char const*
|
||||
trim_front(char const* it, char const* end);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
char const*
|
||||
trim_back(
|
||||
char const* it, char const* first);
|
||||
|
||||
static
|
||||
string_view
|
||||
make_string(char const* first, char const* last)
|
||||
{
|
||||
return {first, static_cast<
|
||||
std::size_t>(last - first)};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
bool
|
||||
is_pathchar(char c);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
bool
|
||||
unhex(unsigned char& d, char c);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
std::pair<char const*, bool>
|
||||
find_fast(
|
||||
char const* buf,
|
||||
char const* buf_end,
|
||||
char const* ranges,
|
||||
size_t ranges_size);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
char const*
|
||||
find_eol(
|
||||
char const* it, char const* last,
|
||||
error_code& ec);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
char const*
|
||||
find_eom(char const* p, char const* last);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
char const*
|
||||
parse_token_to_eol(
|
||||
char const* p,
|
||||
char const* last,
|
||||
char const*& token_last,
|
||||
error_code& ec);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
bool
|
||||
parse_dec(string_view s, std::uint64_t& v);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
bool
|
||||
parse_hex(char const*& it, std::uint64_t& v);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
bool
|
||||
parse_crlf(char const*& it);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
void
|
||||
parse_method(
|
||||
char const*& it, char const* last,
|
||||
string_view& result, error_code& ec);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
void
|
||||
parse_target(
|
||||
char const*& it, char const* last,
|
||||
string_view& result, error_code& ec);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
void
|
||||
parse_version(
|
||||
char const*& it, char const* last,
|
||||
int& result, error_code& ec);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
void
|
||||
parse_status(
|
||||
char const*& it, char const* last,
|
||||
unsigned short& result, error_code& ec);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
void
|
||||
parse_reason(
|
||||
char const*& it, char const* last,
|
||||
string_view& result, error_code& ec);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
void
|
||||
parse_field(
|
||||
char const*& p,
|
||||
char const* last,
|
||||
string_view& name,
|
||||
string_view& value,
|
||||
beast::detail::char_buffer<max_obs_fold>& buf,
|
||||
error_code& ec);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
void
|
||||
parse_chunk_extensions(
|
||||
char const*& it,
|
||||
char const* last,
|
||||
error_code& ec);
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#ifdef BOOST_BEAST_HEADER_ONLY
|
||||
#include <boost/beast/http/detail/basic_parser.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+849
@@ -0,0 +1,849 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DETAIL_BASIC_PARSER_IPP
|
||||
#define BOOST_BEAST_HTTP_DETAIL_BASIC_PARSER_IPP
|
||||
|
||||
#include <boost/beast/http/detail/basic_parser.hpp>
|
||||
#include <limits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
char const*
|
||||
basic_parser_base::
|
||||
trim_front(char const* it, char const* end)
|
||||
{
|
||||
while(it != end)
|
||||
{
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
char const*
|
||||
basic_parser_base::
|
||||
trim_back(
|
||||
char const* it, char const* first)
|
||||
{
|
||||
while(it != first)
|
||||
{
|
||||
auto const c = it[-1];
|
||||
if(c != ' ' && c != '\t')
|
||||
break;
|
||||
--it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
bool
|
||||
basic_parser_base::
|
||||
is_pathchar(char c)
|
||||
{
|
||||
// VFALCO This looks the same as the one below...
|
||||
|
||||
// TEXT = <any OCTET except CTLs, and excluding LWS>
|
||||
static bool constexpr tab[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
bool
|
||||
basic_parser_base::
|
||||
unhex(unsigned char& d, char c)
|
||||
{
|
||||
static signed char constexpr tab[256] = {
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 16
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 32
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 48
|
||||
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 64
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 80
|
||||
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 96
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 112
|
||||
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 128
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 144
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 160
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 176
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 192
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 208
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 224
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 240
|
||||
};
|
||||
d = static_cast<unsigned char>(
|
||||
tab[static_cast<unsigned char>(c)]);
|
||||
return d != static_cast<unsigned char>(-1);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
std::pair<char const*, bool>
|
||||
basic_parser_base::
|
||||
find_fast(
|
||||
char const* buf,
|
||||
char const* buf_end,
|
||||
char const* ranges,
|
||||
size_t ranges_size)
|
||||
{
|
||||
bool found = false;
|
||||
boost::ignore_unused(buf_end, ranges, ranges_size);
|
||||
return {buf, found};
|
||||
}
|
||||
|
||||
// VFALCO Can SIMD help this?
|
||||
char const*
|
||||
basic_parser_base::
|
||||
find_eol(
|
||||
char const* it, char const* last,
|
||||
error_code& ec)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
ec = {};
|
||||
return nullptr;
|
||||
}
|
||||
if(*it == '\r')
|
||||
{
|
||||
if(++it == last)
|
||||
{
|
||||
ec = {};
|
||||
return nullptr;
|
||||
}
|
||||
if(*it != '\n')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_line_ending);
|
||||
return nullptr;
|
||||
}
|
||||
ec = {};
|
||||
return ++it;
|
||||
}
|
||||
// VFALCO Should we handle the legacy case
|
||||
// for lines terminated with a single '\n'?
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
basic_parser_base::
|
||||
parse_dec(
|
||||
string_view s,
|
||||
std::uint64_t& v)
|
||||
{
|
||||
char const* it = s.data();
|
||||
char const* last = it + s.size();
|
||||
if(it == last)
|
||||
return false;
|
||||
std::uint64_t tmp = 0;
|
||||
do
|
||||
{
|
||||
if((! is_digit(*it)) ||
|
||||
tmp > (std::numeric_limits<std::uint64_t>::max)() / 10)
|
||||
return false;
|
||||
tmp *= 10;
|
||||
std::uint64_t const d = *it - '0';
|
||||
if((std::numeric_limits<std::uint64_t>::max)() - tmp < d)
|
||||
return false;
|
||||
tmp += d;
|
||||
}
|
||||
while(++it != last);
|
||||
v = tmp;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
basic_parser_base::
|
||||
parse_hex(char const*& it, std::uint64_t& v)
|
||||
{
|
||||
unsigned char d;
|
||||
if(! unhex(d, *it))
|
||||
return false;
|
||||
std::uint64_t tmp = 0;
|
||||
do
|
||||
{
|
||||
if(tmp > (std::numeric_limits<std::uint64_t>::max)() / 16)
|
||||
return false;
|
||||
tmp *= 16;
|
||||
if((std::numeric_limits<std::uint64_t>::max)() - tmp < d)
|
||||
return false;
|
||||
tmp += d;
|
||||
}
|
||||
while(unhex(d, *++it));
|
||||
v = tmp;
|
||||
return true;
|
||||
}
|
||||
|
||||
char const*
|
||||
basic_parser_base::
|
||||
find_eom(char const* p, char const* last)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(p + 4 > last)
|
||||
return nullptr;
|
||||
if(p[3] != '\n')
|
||||
{
|
||||
if(p[3] == '\r')
|
||||
++p;
|
||||
else
|
||||
p += 4;
|
||||
}
|
||||
else if(p[2] != '\r')
|
||||
{
|
||||
p += 4;
|
||||
}
|
||||
else if(p[1] != '\n')
|
||||
{
|
||||
p += 2;
|
||||
}
|
||||
else if(p[0] != '\r')
|
||||
{
|
||||
p += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
return p + 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
char const*
|
||||
basic_parser_base::
|
||||
parse_token_to_eol(
|
||||
char const* p,
|
||||
char const* last,
|
||||
char const*& token_last,
|
||||
error_code& ec)
|
||||
{
|
||||
for(;; ++p)
|
||||
{
|
||||
if(p >= last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return p;
|
||||
}
|
||||
if(BOOST_UNLIKELY(! is_print(*p)))
|
||||
if((BOOST_LIKELY(static_cast<
|
||||
unsigned char>(*p) < '\040') &&
|
||||
BOOST_LIKELY(*p != 9)) ||
|
||||
BOOST_UNLIKELY(*p == 127))
|
||||
goto found_control;
|
||||
}
|
||||
found_control:
|
||||
if(BOOST_LIKELY(*p == '\r'))
|
||||
{
|
||||
if(++p >= last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return last;
|
||||
}
|
||||
if(*p++ != '\n')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_line_ending);
|
||||
return last;
|
||||
}
|
||||
token_last = p - 2;
|
||||
}
|
||||
#if 0
|
||||
// VFALCO This allows `\n` by itself
|
||||
// to terminate a line
|
||||
else if(*p == '\n')
|
||||
{
|
||||
token_last = p;
|
||||
++p;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
// invalid character
|
||||
return nullptr;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
bool
|
||||
basic_parser_base::
|
||||
parse_crlf(char const*& it)
|
||||
{
|
||||
if( it[0] != '\r' || it[1] != '\n')
|
||||
return false;
|
||||
it += 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
basic_parser_base::
|
||||
parse_method(
|
||||
char const*& it, char const* last,
|
||||
string_view& result, error_code& ec)
|
||||
{
|
||||
// parse token SP
|
||||
auto const first = it;
|
||||
for(;; ++it)
|
||||
{
|
||||
if(it + 1 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
if(it + 1 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(*it != ' ')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_method);
|
||||
return;
|
||||
}
|
||||
if(it == first)
|
||||
{
|
||||
// cannot be empty
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_method);
|
||||
return;
|
||||
}
|
||||
result = make_string(first, it++);
|
||||
}
|
||||
|
||||
void
|
||||
basic_parser_base::
|
||||
parse_target(
|
||||
char const*& it, char const* last,
|
||||
string_view& result, error_code& ec)
|
||||
{
|
||||
// parse target SP
|
||||
auto const first = it;
|
||||
for(;; ++it)
|
||||
{
|
||||
if(it + 1 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(! is_pathchar(*it))
|
||||
break;
|
||||
}
|
||||
if(it + 1 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(*it != ' ')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_target);
|
||||
return;
|
||||
}
|
||||
if(it == first)
|
||||
{
|
||||
// cannot be empty
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_target);
|
||||
return;
|
||||
}
|
||||
result = make_string(first, it++);
|
||||
}
|
||||
|
||||
void
|
||||
basic_parser_base::
|
||||
parse_version(
|
||||
char const*& it, char const* last,
|
||||
int& result, error_code& ec)
|
||||
{
|
||||
if(it + 8 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(*it++ != 'H')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
if(*it++ != 'T')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
if(*it++ != 'T')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
if(*it++ != 'P')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
if(*it++ != '/')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
result = 10 * (*it++ - '0');
|
||||
if(*it++ != '.')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
result += *it++ - '0';
|
||||
}
|
||||
|
||||
void
|
||||
basic_parser_base::
|
||||
parse_status(
|
||||
char const*& it, char const* last,
|
||||
unsigned short& result, error_code& ec)
|
||||
{
|
||||
// parse 3(digit) SP
|
||||
if(it + 4 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_status);
|
||||
return;
|
||||
}
|
||||
result = 100 * (*it++ - '0');
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_status);
|
||||
return;
|
||||
}
|
||||
result += 10 * (*it++ - '0');
|
||||
if(! is_digit(*it))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_status);
|
||||
return;
|
||||
}
|
||||
result += *it++ - '0';
|
||||
if(*it++ != ' ')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
basic_parser_base::
|
||||
parse_reason(
|
||||
char const*& it, char const* last,
|
||||
string_view& result, error_code& ec)
|
||||
{
|
||||
auto const first = it;
|
||||
char const* token_last = nullptr;
|
||||
auto p = parse_token_to_eol(
|
||||
it, last, token_last, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! p)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_reason);
|
||||
return;
|
||||
}
|
||||
result = make_string(first, token_last);
|
||||
it = p;
|
||||
}
|
||||
|
||||
void
|
||||
basic_parser_base::
|
||||
parse_field(
|
||||
char const*& p,
|
||||
char const* last,
|
||||
string_view& name,
|
||||
string_view& value,
|
||||
beast::detail::char_buffer<max_obs_fold>& buf,
|
||||
error_code& ec)
|
||||
{
|
||||
/* header-field = field-name ":" OWS field-value OWS
|
||||
|
||||
field-name = token
|
||||
field-value = *( field-content / obs-fold )
|
||||
field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
||||
field-vchar = VCHAR / obs-text
|
||||
|
||||
obs-fold = CRLF 1*( SP / HTAB )
|
||||
; obsolete line folding
|
||||
; see Section 3.2.4
|
||||
|
||||
token = 1*<any CHAR except CTLs or separators>
|
||||
CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||
sep = "(" | ")" | "<" | ">" | "@"
|
||||
| "," | ";" | ":" | "\" | <">
|
||||
| "/" | "[" | "]" | "?" | "="
|
||||
| "{" | "}" | SP | HT
|
||||
*/
|
||||
static char const* is_token =
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
|
||||
"\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
|
||||
"\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
|
||||
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||
|
||||
// name
|
||||
BOOST_ALIGNMENT(16) static const char ranges1[] =
|
||||
"\x00 " /* control chars and up to SP */
|
||||
"\"\"" /* 0x22 */
|
||||
"()" /* 0x28,0x29 */
|
||||
",," /* 0x2c */
|
||||
"//" /* 0x2f */
|
||||
":@" /* 0x3a-0x40 */
|
||||
"[]" /* 0x5b-0x5d */
|
||||
"{\377"; /* 0x7b-0xff */
|
||||
auto first = p;
|
||||
bool found;
|
||||
std::tie(p, found) = find_fast(
|
||||
p, last, ranges1, sizeof(ranges1)-1);
|
||||
if(! found && p >= last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
if(*p == ':')
|
||||
break;
|
||||
if(! is_token[static_cast<
|
||||
unsigned char>(*p)])
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_field);
|
||||
return;
|
||||
}
|
||||
++p;
|
||||
if(p >= last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(p == first)
|
||||
{
|
||||
// empty name
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_field);
|
||||
return;
|
||||
}
|
||||
name = make_string(first, p);
|
||||
++p; // eat ':'
|
||||
char const* token_last = nullptr;
|
||||
for(;;)
|
||||
{
|
||||
// eat leading ' ' and '\t'
|
||||
for(;;++p)
|
||||
{
|
||||
if(p + 1 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(! (*p == ' ' || *p == '\t'))
|
||||
break;
|
||||
}
|
||||
// parse to CRLF
|
||||
first = p;
|
||||
p = parse_token_to_eol(p, last, token_last, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! p)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_value);
|
||||
return;
|
||||
}
|
||||
// Look 1 char past the CRLF to handle obs-fold.
|
||||
if(p + 1 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
token_last =
|
||||
trim_back(token_last, first);
|
||||
if(*p != ' ' && *p != '\t')
|
||||
{
|
||||
value = make_string(first, token_last);
|
||||
return;
|
||||
}
|
||||
++p;
|
||||
if(token_last != first)
|
||||
break;
|
||||
}
|
||||
buf.clear();
|
||||
if (!buf.try_append(first, token_last))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(! buf.empty());
|
||||
for(;;)
|
||||
{
|
||||
// eat leading ' ' and '\t'
|
||||
for(;;++p)
|
||||
{
|
||||
if(p + 1 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(! (*p == ' ' || *p == '\t'))
|
||||
break;
|
||||
}
|
||||
// parse to CRLF
|
||||
first = p;
|
||||
p = parse_token_to_eol(p, last, token_last, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! p)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_value);
|
||||
return;
|
||||
}
|
||||
// Look 1 char past the CRLF to handle obs-fold.
|
||||
if(p + 1 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
token_last = trim_back(token_last, first);
|
||||
if(first != token_last)
|
||||
{
|
||||
if (!buf.try_push_back(' ') ||
|
||||
!buf.try_append(first, token_last))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(*p != ' ' && *p != '\t')
|
||||
{
|
||||
value = {buf.data(), buf.size()};
|
||||
return;
|
||||
}
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
basic_parser_base::
|
||||
parse_chunk_extensions(
|
||||
char const*& it,
|
||||
char const* last,
|
||||
error_code& ec)
|
||||
{
|
||||
/*
|
||||
chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
|
||||
BWS = *( SP / HTAB ) ; "Bad White Space"
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
token = 1*tchar
|
||||
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
obs-text = %x80-FF
|
||||
|
||||
https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
|
||||
*/
|
||||
loop:
|
||||
if(it == last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t' && *it != ';')
|
||||
return;
|
||||
// BWS
|
||||
if(*it == ' ' || *it == '\t')
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
}
|
||||
}
|
||||
// ';'
|
||||
if(*it != ';')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return;
|
||||
}
|
||||
semi:
|
||||
++it; // skip ';'
|
||||
// BWS
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
// chunk-ext-name
|
||||
if(! detail::is_token_char(*it))
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
// BWS [ ";" / "=" ]
|
||||
{
|
||||
bool bws;
|
||||
if(*it == ' ' || *it == '\t')
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
}
|
||||
bws = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bws = false;
|
||||
}
|
||||
if(*it == ';')
|
||||
goto semi;
|
||||
if(*it != '=')
|
||||
{
|
||||
if(bws)
|
||||
ec = error::bad_chunk_extension;
|
||||
return;
|
||||
}
|
||||
++it; // skip '='
|
||||
}
|
||||
// BWS
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
// chunk-ext-val
|
||||
if(*it != '"')
|
||||
{
|
||||
// token
|
||||
if(! detail::is_token_char(*it))
|
||||
{
|
||||
ec = error::bad_chunk_extension;
|
||||
return;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// quoted-string
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
if(*it == '"')
|
||||
break;
|
||||
if(*it == '\\')
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
ec = error::need_more;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
goto loop;
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
|
||||
#define BOOST_BEAST_HTTP_DETAIL_CHUNK_ENCODE_HPP
|
||||
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
struct chunk_extensions
|
||||
{
|
||||
virtual ~chunk_extensions() = default;
|
||||
virtual net::const_buffer str() = 0;
|
||||
};
|
||||
|
||||
template<class ChunkExtensions>
|
||||
struct chunk_extensions_impl : chunk_extensions
|
||||
{
|
||||
ChunkExtensions ext_;
|
||||
|
||||
chunk_extensions_impl(ChunkExtensions&& ext) noexcept
|
||||
: ext_(std::move(ext))
|
||||
{
|
||||
}
|
||||
|
||||
chunk_extensions_impl(ChunkExtensions const& ext)
|
||||
: ext_(ext)
|
||||
{
|
||||
}
|
||||
|
||||
net::const_buffer
|
||||
str() override
|
||||
{
|
||||
auto const s = ext_.str();
|
||||
return {s.data(), s.size()};
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, class = void>
|
||||
struct is_chunk_extensions : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_chunk_extensions<T, beast::detail::void_t<decltype(
|
||||
std::declval<string_view&>() = std::declval<T&>().str()
|
||||
)>> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A buffer sequence containing a chunk-encoding header
|
||||
*/
|
||||
class chunk_size
|
||||
{
|
||||
template<class OutIter>
|
||||
static
|
||||
OutIter
|
||||
to_hex(OutIter last, std::size_t n)
|
||||
{
|
||||
if(n == 0)
|
||||
{
|
||||
*--last = '0';
|
||||
return last;
|
||||
}
|
||||
while(n)
|
||||
{
|
||||
*--last = "0123456789abcdef"[n&0xf];
|
||||
n>>=4;
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
struct sequence
|
||||
{
|
||||
net::const_buffer b;
|
||||
char data[1 + 2 * sizeof(std::size_t)];
|
||||
|
||||
explicit
|
||||
sequence(std::size_t n)
|
||||
{
|
||||
char* it0 = data + sizeof(data);
|
||||
auto it = to_hex(it0, n);
|
||||
b = {it,
|
||||
static_cast<std::size_t>(it0 - it)};
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<sequence> sp_;
|
||||
|
||||
public:
|
||||
using value_type = net::const_buffer;
|
||||
|
||||
using const_iterator = value_type const*;
|
||||
|
||||
chunk_size(chunk_size const& other) = default;
|
||||
|
||||
/** Construct a chunk header
|
||||
|
||||
@param n The number of octets in this chunk.
|
||||
*/
|
||||
chunk_size(std::size_t n)
|
||||
: sp_(std::make_shared<sequence>(n))
|
||||
{
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return &sp_->b;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return begin() + 1;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Returns a buffer sequence holding a CRLF for chunk encoding
|
||||
inline
|
||||
net::const_buffer const&
|
||||
chunk_crlf()
|
||||
{
|
||||
static net::const_buffer const cb{"\r\n", 2};
|
||||
return cb;
|
||||
}
|
||||
|
||||
/// Returns a buffer sequence holding a final chunk header
|
||||
inline
|
||||
net::const_buffer const&
|
||||
chunk_last()
|
||||
{
|
||||
static net::const_buffer const cb{"0\r\n", 3};
|
||||
return cb;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if 0
|
||||
template<class = void>
|
||||
struct chunk_crlf_iter_type
|
||||
{
|
||||
class value_type
|
||||
{
|
||||
char const s[2] = {'\r', '\n'};
|
||||
|
||||
public:
|
||||
value_type() = default;
|
||||
|
||||
operator
|
||||
net::const_buffer() const
|
||||
{
|
||||
return {s, sizeof(s)};
|
||||
}
|
||||
};
|
||||
static value_type value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
typename chunk_crlf_iter_type<T>::value_type
|
||||
chunk_crlf_iter_type<T>::value;
|
||||
|
||||
using chunk_crlf_iter = chunk_crlf_iter_type<void>;
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct chunk_size0
|
||||
{
|
||||
using value_type = net::const_buffer;
|
||||
using const_iterator = value_type const*;
|
||||
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return &chunk_last();
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return begin() + 1;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class T,
|
||||
bool = is_fields<T>::value>
|
||||
struct buffers_or_fields
|
||||
{
|
||||
using type = typename
|
||||
T::writer::const_buffers_type;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct buffers_or_fields<T, false>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DETAIL_RFC7230_HPP
|
||||
#define BOOST_BEAST_HTTP_DETAIL_RFC7230_HPP
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
bool
|
||||
is_digit(char c);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
char
|
||||
is_alpha(char c);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
char
|
||||
is_text(char c);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
char
|
||||
is_token_char(char c);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
char
|
||||
is_qdchar(char c);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
char
|
||||
is_qpchar(char c);
|
||||
|
||||
|
||||
// converts to lower case,
|
||||
// returns 0 if not a valid text char
|
||||
//
|
||||
BOOST_BEAST_DECL
|
||||
char
|
||||
to_value_char(char c);
|
||||
|
||||
// VFALCO TODO Make this return unsigned?
|
||||
BOOST_BEAST_DECL
|
||||
std::int8_t
|
||||
unhex(char c);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
string_view
|
||||
trim(string_view s);
|
||||
|
||||
struct param_iter
|
||||
{
|
||||
using iter_type = string_view::const_iterator;
|
||||
|
||||
iter_type it;
|
||||
iter_type first;
|
||||
iter_type last;
|
||||
std::pair<string_view, string_view> v;
|
||||
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return first == it;
|
||||
}
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
increment();
|
||||
};
|
||||
|
||||
/*
|
||||
#token = [ ( "," / token ) *( OWS "," [ OWS token ] ) ]
|
||||
*/
|
||||
struct opt_token_list_policy
|
||||
{
|
||||
using value_type = string_view;
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
bool
|
||||
operator()(value_type& v,
|
||||
char const*& it, string_view s) const;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#ifdef BOOST_BEAST_HEADER_ONLY
|
||||
#include <boost/beast/http/detail/rfc7230.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+390
@@ -0,0 +1,390 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DETAIL_RFC7230_IPP
|
||||
#define BOOST_BEAST_HTTP_DETAIL_RFC7230_IPP
|
||||
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
bool
|
||||
is_digit(char c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
char
|
||||
is_alpha(char c)
|
||||
{
|
||||
static char constexpr tab[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 48
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 80
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 112
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
char
|
||||
is_text(char c)
|
||||
{
|
||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||
static char constexpr tab[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
char
|
||||
is_token_char(char c)
|
||||
{
|
||||
/*
|
||||
tchar = "!" | "#" | "$" | "%" | "&" |
|
||||
"'" | "*" | "+" | "-" | "." |
|
||||
"^" | "_" | "`" | "|" | "~" |
|
||||
DIGIT | ALPHA
|
||||
*/
|
||||
static char constexpr tab[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, // 112
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 144
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 160
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 176
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 192
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 208
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 224
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
char
|
||||
is_qdchar(char c)
|
||||
{
|
||||
/*
|
||||
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||
*/
|
||||
static char constexpr tab[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
char
|
||||
is_qpchar(char c)
|
||||
{
|
||||
/*
|
||||
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
obs-text = %x80-FF
|
||||
*/
|
||||
static char constexpr tab[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
// converts to lower case,
|
||||
// returns 0 if not a valid text char
|
||||
//
|
||||
char
|
||||
to_value_char(char c)
|
||||
{
|
||||
// TEXT = <any OCTET except CTLs, but including LWS>
|
||||
static unsigned char constexpr tab[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, // 32
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // 48
|
||||
64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 64
|
||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 91, 92, 93, 94, 95, // 80
|
||||
96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, // 96
|
||||
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 0, // 112
|
||||
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, // 128
|
||||
144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, // 144
|
||||
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, // 160
|
||||
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, // 176
|
||||
192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, // 192
|
||||
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, // 208
|
||||
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, // 224
|
||||
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return static_cast<char>(tab[static_cast<unsigned char>(c)]);
|
||||
}
|
||||
|
||||
// VFALCO TODO Make this return unsigned?
|
||||
std::int8_t
|
||||
unhex(char c)
|
||||
{
|
||||
static signed char constexpr tab[] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, // 48
|
||||
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80
|
||||
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240
|
||||
};
|
||||
BOOST_STATIC_ASSERT(sizeof(tab) == 256);
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
template <class ForwardIt>
|
||||
void
|
||||
skip_ows(ForwardIt& it, ForwardIt end)
|
||||
{
|
||||
while(it != end)
|
||||
{
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
template <class ForwardIt>
|
||||
void
|
||||
skip_token(ForwardIt& it, ForwardIt last)
|
||||
{
|
||||
while(it != last && is_token_char(*it))
|
||||
++it;
|
||||
}
|
||||
|
||||
string_view
|
||||
trim(string_view s)
|
||||
{
|
||||
auto first = s.begin();
|
||||
auto last = s.end();
|
||||
skip_ows(first, last);
|
||||
while(first != last)
|
||||
{
|
||||
auto const c = *std::prev(last);
|
||||
if(c != ' ' && c != '\t')
|
||||
break;
|
||||
--last;
|
||||
}
|
||||
if(first == last)
|
||||
return {};
|
||||
return {&*first,
|
||||
static_cast<std::size_t>(last - first)};
|
||||
}
|
||||
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
param_iter::
|
||||
increment()
|
||||
{
|
||||
/*
|
||||
param-list = *( OWS ";" OWS param )
|
||||
param = token OWS [ "=" OWS ( token / quoted-string ) ]
|
||||
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
obs-text = %x80-FF
|
||||
*/
|
||||
auto const err =
|
||||
[&]
|
||||
{
|
||||
it = first;
|
||||
};
|
||||
v.first = {};
|
||||
v.second = {};
|
||||
detail::skip_ows(it, last);
|
||||
first = it;
|
||||
if(it == last)
|
||||
return err();
|
||||
if(*it != ';')
|
||||
return err();
|
||||
++it;
|
||||
detail::skip_ows(it, last);
|
||||
if(it == last)
|
||||
return err();
|
||||
// param
|
||||
if(! detail::is_token_char(*it))
|
||||
return err();
|
||||
auto const p0 = it;
|
||||
skip_token(++it, last);
|
||||
auto const p1 = it;
|
||||
v.first = { &*p0, static_cast<std::size_t>(p1 - p0) };
|
||||
detail::skip_ows(it, last);
|
||||
if(it == last)
|
||||
return;
|
||||
if(*it == ';')
|
||||
return;
|
||||
if(*it != '=')
|
||||
return err();
|
||||
++it;
|
||||
detail::skip_ows(it, last);
|
||||
if(it == last)
|
||||
return;
|
||||
if(*it == '"')
|
||||
{
|
||||
// quoted-string
|
||||
auto const p2 = it;
|
||||
++it;
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
return err();
|
||||
auto c = *it++;
|
||||
if(c == '"')
|
||||
break;
|
||||
if(detail::is_qdchar(c))
|
||||
continue;
|
||||
if(c != '\\')
|
||||
return err();
|
||||
if(it == last)
|
||||
return err();
|
||||
c = *it++;
|
||||
if(! detail::is_qpchar(c))
|
||||
return err();
|
||||
}
|
||||
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
|
||||
}
|
||||
else
|
||||
{
|
||||
// token
|
||||
if(! detail::is_token_char(*it))
|
||||
return err();
|
||||
auto const p2 = it;
|
||||
skip_token(++it, last);
|
||||
v.second = { &*p2, static_cast<std::size_t>(it - p2) };
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
opt_token_list_policy::operator()(value_type& v,
|
||||
char const*& it, string_view s) const
|
||||
{
|
||||
v = {};
|
||||
auto need_comma = it != s.data();
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it, (s.data() + s.size()));
|
||||
if(it == (s.data() + s.size()))
|
||||
{
|
||||
it = nullptr;
|
||||
return true;
|
||||
}
|
||||
auto const c = *it;
|
||||
if(detail::is_token_char(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return false;
|
||||
auto const p0 = it;
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == (s.data() + s.size()))
|
||||
break;
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
v = string_view{p0,
|
||||
static_cast<std::size_t>(it - p0)};
|
||||
return true;
|
||||
}
|
||||
if(c != ',')
|
||||
return false;
|
||||
need_comma = false;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif // BOOST_BEAST_HTTP_DETAIL_RFC7230_IPP
|
||||
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DETAIL_TYPE_TRAITS_HPP
|
||||
#define BOOST_BEAST_HTTP_DETAIL_TYPE_TRAITS_HPP
|
||||
|
||||
#include <boost/beast/core/detail/type_traits.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
class header;
|
||||
|
||||
template<bool, class, class>
|
||||
class message;
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
class parser;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class T>
|
||||
class is_header_impl
|
||||
{
|
||||
template<bool b, class F>
|
||||
static std::true_type check(
|
||||
header<b, F> const*);
|
||||
static std::false_type check(...);
|
||||
public:
|
||||
using type = decltype(check((T*)0));
|
||||
};
|
||||
|
||||
template<class T>
|
||||
using is_header = typename is_header_impl<T>::type;
|
||||
|
||||
template<class T>
|
||||
struct is_parser : std::false_type {};
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
struct is_parser<parser<isRequest, Body, Fields>> : std::true_type {};
|
||||
|
||||
struct fields_model
|
||||
{
|
||||
struct writer;
|
||||
|
||||
string_view method() const;
|
||||
string_view reason() const;
|
||||
string_view target() const;
|
||||
|
||||
protected:
|
||||
string_view get_method_impl() const;
|
||||
string_view get_target_impl() const;
|
||||
string_view get_reason_impl() const;
|
||||
bool get_chunked_impl() const;
|
||||
bool get_keep_alive_impl(unsigned) const;
|
||||
bool has_content_length_impl() const;
|
||||
void set_method_impl(string_view);
|
||||
void set_target_impl(string_view);
|
||||
void set_reason_impl(string_view);
|
||||
void set_chunked_impl(bool);
|
||||
void set_content_length_impl(boost::optional<std::uint64_t>);
|
||||
void set_keep_alive_impl(unsigned, bool);
|
||||
};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct has_value_type : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct has_value_type<T, beast::detail::void_t<
|
||||
typename T::value_type
|
||||
> > : std::true_type {};
|
||||
|
||||
/** Determine if a <em>Body</em> type has a size
|
||||
|
||||
This metafunction is equivalent to `std::true_type` if
|
||||
Body contains a static member function called `size`.
|
||||
*/
|
||||
template<class T, class = void>
|
||||
struct is_body_sized : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_body_sized<T, beast::detail::void_t<
|
||||
typename T::value_type,
|
||||
decltype(
|
||||
std::declval<std::uint64_t&>() =
|
||||
T::size(std::declval<typename T::value_type const&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_fields_helper : T
|
||||
{
|
||||
template<class U = is_fields_helper>
|
||||
static auto f1(int) -> decltype(
|
||||
std::declval<string_view&>() = std::declval<U const&>().get_method_impl(),
|
||||
std::true_type());
|
||||
static auto f1(...) -> std::false_type;
|
||||
using t1 = decltype(f1(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f2(int) -> decltype(
|
||||
std::declval<string_view&>() = std::declval<U const&>().get_target_impl(),
|
||||
std::true_type());
|
||||
static auto f2(...) -> std::false_type;
|
||||
using t2 = decltype(f2(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f3(int) -> decltype(
|
||||
std::declval<string_view&>() = std::declval<U const&>().get_reason_impl(),
|
||||
std::true_type());
|
||||
static auto f3(...) -> std::false_type;
|
||||
using t3 = decltype(f3(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f4(int) -> decltype(
|
||||
std::declval<bool&>() = std::declval<U const&>().get_chunked_impl(),
|
||||
std::true_type());
|
||||
static auto f4(...) -> std::false_type;
|
||||
using t4 = decltype(f4(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f5(int) -> decltype(
|
||||
std::declval<bool&>() = std::declval<U const&>().get_keep_alive_impl(
|
||||
std::declval<unsigned>()),
|
||||
std::true_type());
|
||||
static auto f5(...) -> std::false_type;
|
||||
using t5 = decltype(f5(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f6(int) -> decltype(
|
||||
std::declval<bool&>() = std::declval<U const&>().has_content_length_impl(),
|
||||
std::true_type());
|
||||
static auto f6(...) -> std::false_type;
|
||||
using t6 = decltype(f6(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f7(int) -> decltype(
|
||||
void(std::declval<U&>().set_method_impl(std::declval<string_view>())),
|
||||
std::true_type());
|
||||
static auto f7(...) -> std::false_type;
|
||||
using t7 = decltype(f7(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f8(int) -> decltype(
|
||||
void(std::declval<U&>().set_target_impl(std::declval<string_view>())),
|
||||
std::true_type());
|
||||
static auto f8(...) -> std::false_type;
|
||||
using t8 = decltype(f8(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f9(int) -> decltype(
|
||||
void(std::declval<U&>().set_reason_impl(std::declval<string_view>())),
|
||||
std::true_type());
|
||||
static auto f9(...) -> std::false_type;
|
||||
using t9 = decltype(f9(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f10(int) -> decltype(
|
||||
void(std::declval<U&>().set_chunked_impl(std::declval<bool>())),
|
||||
std::true_type());
|
||||
static auto f10(...) -> std::false_type;
|
||||
using t10 = decltype(f10(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f11(int) -> decltype(
|
||||
void(std::declval<U&>().set_content_length_impl(
|
||||
std::declval<boost::optional<std::uint64_t>>())),
|
||||
std::true_type());
|
||||
static auto f11(...) -> std::false_type;
|
||||
using t11 = decltype(f11(0));
|
||||
|
||||
template<class U = is_fields_helper>
|
||||
static auto f12(int) -> decltype(
|
||||
void(std::declval<U&>().set_keep_alive_impl(
|
||||
std::declval<unsigned>(),
|
||||
std::declval<bool>())),
|
||||
std::true_type());
|
||||
static auto f12(...) -> std::false_type;
|
||||
using t12 = decltype(f12(0));
|
||||
|
||||
using type = std::integral_constant<bool,
|
||||
t1::value && t2::value && t3::value &&
|
||||
t4::value && t5::value && t6::value &&
|
||||
t7::value && t8::value && t9::value &&
|
||||
t10::value && t11::value && t12::value>;
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_DYNAMIC_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_DYNAMIC_BODY_HPP
|
||||
|
||||
#include <boost/beast/core/multi_buffer.hpp>
|
||||
#include <boost/beast/http/basic_dynamic_body.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A dynamic message body represented by a @ref multi_buffer
|
||||
|
||||
Meets the requirements of <em>Body</em>.
|
||||
*/
|
||||
using dynamic_body = basic_dynamic_body<multi_buffer>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_EMPTY_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_EMPTY_BODY_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** An empty <em>Body</em>
|
||||
|
||||
This body is used to represent messages which do not have a
|
||||
message body. If this body is used with a parser, and the
|
||||
parser encounters octets corresponding to a message body,
|
||||
the parser will fail with the error @ref http::unexpected_body.
|
||||
|
||||
The Content-Length of this body is always 0.
|
||||
*/
|
||||
struct empty_body
|
||||
{
|
||||
/** The type of container used for the body
|
||||
|
||||
This determines the type of @ref message::body
|
||||
when this body type is used with a message container.
|
||||
*/
|
||||
struct value_type
|
||||
{
|
||||
};
|
||||
|
||||
/** Returns the payload size of the body
|
||||
|
||||
When this body is used with @ref message::prepare_payload,
|
||||
the Content-Length will be set to the payload size, and
|
||||
any chunked Transfer-Encoding will be removed.
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of <em>BodyReader</em>.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using reader = __implementation_defined__;
|
||||
#else
|
||||
struct reader
|
||||
{
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(header<isRequest, Fields>&, value_type&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<std::uint64_t> const&, error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const&,
|
||||
error_code& ec)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::unexpected_body);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of <em>BodyWriter</em>.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using writer = __implementation_defined__;
|
||||
#else
|
||||
struct writer
|
||||
{
|
||||
using const_buffers_type =
|
||||
net::const_buffer;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(header<isRequest, Fields> const&, value_type const&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
return boost::none;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_ERROR_HPP
|
||||
#define BOOST_BEAST_HTTP_ERROR_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/// Error codes returned from HTTP algorithms and operations.
|
||||
enum class error
|
||||
{
|
||||
/** The end of the stream was reached.
|
||||
|
||||
This error is returned when attempting to read HTTP data,
|
||||
and the stream returns the error `net::error::eof`
|
||||
before any octets corresponding to a new HTTP message have
|
||||
been received.
|
||||
*/
|
||||
end_of_stream = 1,
|
||||
|
||||
/** The incoming message is incomplete.
|
||||
|
||||
This happens when the end of stream is reached during
|
||||
parsing and some octets have been received, but not the
|
||||
entire message.
|
||||
*/
|
||||
partial_message,
|
||||
|
||||
/** Additional buffers are required.
|
||||
|
||||
This error is returned during parsing when additional
|
||||
octets are needed. The caller should append more data
|
||||
to the existing buffer and retry the parse operation.
|
||||
*/
|
||||
need_more,
|
||||
|
||||
/** An unexpected body was encountered during parsing.
|
||||
|
||||
This error is returned when attempting to parse body
|
||||
octets into a message container which has the
|
||||
@ref empty_body body type.
|
||||
|
||||
@see empty_body
|
||||
*/
|
||||
unexpected_body,
|
||||
|
||||
/** Additional buffers are required.
|
||||
|
||||
This error is returned under the following conditions:
|
||||
|
||||
@li During serialization when using @ref buffer_body.
|
||||
The caller should update the body to point to a new
|
||||
buffer or indicate that there are no more octets in
|
||||
the body.
|
||||
|
||||
@li During parsing when using @ref buffer_body.
|
||||
The caller should update the body to point to a new
|
||||
storage area to receive additional body octets.
|
||||
*/
|
||||
need_buffer,
|
||||
|
||||
/** The end of a chunk was reached
|
||||
*/
|
||||
end_of_chunk,
|
||||
|
||||
/** Buffer maximum exceeded.
|
||||
|
||||
This error is returned when reading HTTP content
|
||||
into a dynamic buffer, and the operation would
|
||||
exceed the maximum size of the buffer.
|
||||
*/
|
||||
buffer_overflow,
|
||||
|
||||
/** Header limit exceeded.
|
||||
|
||||
The parser detected an incoming message header which
|
||||
exceeded a configured limit.
|
||||
*/
|
||||
header_limit,
|
||||
|
||||
/** Body limit exceeded.
|
||||
|
||||
The parser detected an incoming message body which
|
||||
exceeded a configured limit.
|
||||
*/
|
||||
body_limit,
|
||||
|
||||
/** A memory allocation failed.
|
||||
|
||||
When basic_fields throws std::bad_alloc, it is
|
||||
converted into this error by @ref parser.
|
||||
*/
|
||||
bad_alloc,
|
||||
|
||||
//
|
||||
// (parser errors)
|
||||
//
|
||||
|
||||
/// The line ending was malformed
|
||||
bad_line_ending,
|
||||
|
||||
/// The method is invalid.
|
||||
bad_method,
|
||||
|
||||
/// The request-target is invalid.
|
||||
bad_target,
|
||||
|
||||
/// The HTTP-version is invalid.
|
||||
bad_version,
|
||||
|
||||
/// The status-code is invalid.
|
||||
bad_status,
|
||||
|
||||
/// The reason-phrase is invalid.
|
||||
bad_reason,
|
||||
|
||||
/// The field name is invalid.
|
||||
bad_field,
|
||||
|
||||
/// The field value is invalid.
|
||||
bad_value,
|
||||
|
||||
/// The Content-Length is invalid.
|
||||
bad_content_length,
|
||||
|
||||
/// The Transfer-Encoding is invalid.
|
||||
bad_transfer_encoding,
|
||||
|
||||
/// The chunk syntax is invalid.
|
||||
bad_chunk,
|
||||
|
||||
/// The chunk extension is invalid.
|
||||
bad_chunk_extension,
|
||||
|
||||
/// An obs-fold exceeded an internal limit.
|
||||
bad_obs_fold,
|
||||
|
||||
/// The response contains multiple and conflicting Content-Length.
|
||||
multiple_content_length,
|
||||
|
||||
/** The parser is stale.
|
||||
|
||||
This happens when attempting to re-use a parser that has
|
||||
already completed parsing a message. Programs must construct
|
||||
a new parser for each message. This can be easily done by
|
||||
storing the parser in an boost or std::optional container.
|
||||
*/
|
||||
stale_parser,
|
||||
|
||||
/** The message body is shorter than expected.
|
||||
|
||||
This error is returned by @ref file_body when an unexpected
|
||||
unexpected end-of-file condition is encountered while trying
|
||||
to read from the file.
|
||||
*/
|
||||
short_read
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/error.hpp>
|
||||
#ifdef BOOST_BEAST_HEADER_ONLY
|
||||
#include <boost/beast/http/impl/error.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+414
@@ -0,0 +1,414 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_FIELD_HPP
|
||||
#define BOOST_BEAST_HTTP_FIELD_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
enum class field : unsigned short
|
||||
{
|
||||
unknown = 0,
|
||||
|
||||
a_im,
|
||||
accept,
|
||||
accept_additions,
|
||||
accept_charset,
|
||||
accept_datetime,
|
||||
accept_encoding,
|
||||
accept_features,
|
||||
accept_language,
|
||||
accept_patch,
|
||||
accept_post,
|
||||
accept_ranges,
|
||||
access_control,
|
||||
access_control_allow_credentials,
|
||||
access_control_allow_headers,
|
||||
access_control_allow_methods,
|
||||
access_control_allow_origin,
|
||||
access_control_expose_headers,
|
||||
access_control_max_age,
|
||||
access_control_request_headers,
|
||||
access_control_request_method,
|
||||
age,
|
||||
allow,
|
||||
alpn,
|
||||
also_control,
|
||||
alt_svc,
|
||||
alt_used,
|
||||
alternate_recipient,
|
||||
alternates,
|
||||
apparently_to,
|
||||
apply_to_redirect_ref,
|
||||
approved,
|
||||
archive,
|
||||
archived_at,
|
||||
article_names,
|
||||
article_updates,
|
||||
authentication_control,
|
||||
authentication_info,
|
||||
authentication_results,
|
||||
authorization,
|
||||
auto_submitted,
|
||||
autoforwarded,
|
||||
autosubmitted,
|
||||
base,
|
||||
bcc,
|
||||
body,
|
||||
c_ext,
|
||||
c_man,
|
||||
c_opt,
|
||||
c_pep,
|
||||
c_pep_info,
|
||||
cache_control,
|
||||
caldav_timezones,
|
||||
cancel_key,
|
||||
cancel_lock,
|
||||
cc,
|
||||
close,
|
||||
comments,
|
||||
compliance,
|
||||
connection,
|
||||
content_alternative,
|
||||
content_base,
|
||||
content_description,
|
||||
content_disposition,
|
||||
content_duration,
|
||||
content_encoding,
|
||||
content_features,
|
||||
content_id,
|
||||
content_identifier,
|
||||
content_language,
|
||||
content_length,
|
||||
content_location,
|
||||
content_md5,
|
||||
content_range,
|
||||
content_return,
|
||||
content_script_type,
|
||||
content_style_type,
|
||||
content_transfer_encoding,
|
||||
content_type,
|
||||
content_version,
|
||||
control,
|
||||
conversion,
|
||||
conversion_with_loss,
|
||||
cookie,
|
||||
cookie2,
|
||||
cost,
|
||||
dasl,
|
||||
date,
|
||||
date_received,
|
||||
dav,
|
||||
default_style,
|
||||
deferred_delivery,
|
||||
delivery_date,
|
||||
delta_base,
|
||||
depth,
|
||||
derived_from,
|
||||
destination,
|
||||
differential_id,
|
||||
digest,
|
||||
discarded_x400_ipms_extensions,
|
||||
discarded_x400_mts_extensions,
|
||||
disclose_recipients,
|
||||
disposition_notification_options,
|
||||
disposition_notification_to,
|
||||
distribution,
|
||||
dkim_signature,
|
||||
dl_expansion_history,
|
||||
downgraded_bcc,
|
||||
downgraded_cc,
|
||||
downgraded_disposition_notification_to,
|
||||
downgraded_final_recipient,
|
||||
downgraded_from,
|
||||
downgraded_in_reply_to,
|
||||
downgraded_mail_from,
|
||||
downgraded_message_id,
|
||||
downgraded_original_recipient,
|
||||
downgraded_rcpt_to,
|
||||
downgraded_references,
|
||||
downgraded_reply_to,
|
||||
downgraded_resent_bcc,
|
||||
downgraded_resent_cc,
|
||||
downgraded_resent_from,
|
||||
downgraded_resent_reply_to,
|
||||
downgraded_resent_sender,
|
||||
downgraded_resent_to,
|
||||
downgraded_return_path,
|
||||
downgraded_sender,
|
||||
downgraded_to,
|
||||
ediint_features,
|
||||
eesst_version,
|
||||
encoding,
|
||||
encrypted,
|
||||
errors_to,
|
||||
etag,
|
||||
expect,
|
||||
expires,
|
||||
expiry_date,
|
||||
ext,
|
||||
followup_to,
|
||||
forwarded,
|
||||
from,
|
||||
generate_delivery_report,
|
||||
getprofile,
|
||||
hobareg,
|
||||
host,
|
||||
http2_settings,
|
||||
if_,
|
||||
if_match,
|
||||
if_modified_since,
|
||||
if_none_match,
|
||||
if_range,
|
||||
if_schedule_tag_match,
|
||||
if_unmodified_since,
|
||||
im,
|
||||
importance,
|
||||
in_reply_to,
|
||||
incomplete_copy,
|
||||
injection_date,
|
||||
injection_info,
|
||||
jabber_id,
|
||||
keep_alive,
|
||||
keywords,
|
||||
label,
|
||||
language,
|
||||
last_modified,
|
||||
latest_delivery_time,
|
||||
lines,
|
||||
link,
|
||||
list_archive,
|
||||
list_help,
|
||||
list_id,
|
||||
list_owner,
|
||||
list_post,
|
||||
list_subscribe,
|
||||
list_unsubscribe,
|
||||
list_unsubscribe_post,
|
||||
location,
|
||||
lock_token,
|
||||
man,
|
||||
max_forwards,
|
||||
memento_datetime,
|
||||
message_context,
|
||||
message_id,
|
||||
message_type,
|
||||
meter,
|
||||
method_check,
|
||||
method_check_expires,
|
||||
mime_version,
|
||||
mmhs_acp127_message_identifier,
|
||||
mmhs_authorizing_users,
|
||||
mmhs_codress_message_indicator,
|
||||
mmhs_copy_precedence,
|
||||
mmhs_exempted_address,
|
||||
mmhs_extended_authorisation_info,
|
||||
mmhs_handling_instructions,
|
||||
mmhs_message_instructions,
|
||||
mmhs_message_type,
|
||||
mmhs_originator_plad,
|
||||
mmhs_originator_reference,
|
||||
mmhs_other_recipients_indicator_cc,
|
||||
mmhs_other_recipients_indicator_to,
|
||||
mmhs_primary_precedence,
|
||||
mmhs_subject_indicator_codes,
|
||||
mt_priority,
|
||||
negotiate,
|
||||
newsgroups,
|
||||
nntp_posting_date,
|
||||
nntp_posting_host,
|
||||
non_compliance,
|
||||
obsoletes,
|
||||
opt,
|
||||
optional,
|
||||
optional_www_authenticate,
|
||||
ordering_type,
|
||||
organization,
|
||||
origin,
|
||||
original_encoded_information_types,
|
||||
original_from,
|
||||
original_message_id,
|
||||
original_recipient,
|
||||
original_sender,
|
||||
original_subject,
|
||||
originator_return_address,
|
||||
overwrite,
|
||||
p3p,
|
||||
path,
|
||||
pep,
|
||||
pep_info,
|
||||
pics_label,
|
||||
position,
|
||||
posting_version,
|
||||
pragma,
|
||||
prefer,
|
||||
preference_applied,
|
||||
prevent_nondelivery_report,
|
||||
priority,
|
||||
privicon,
|
||||
profileobject,
|
||||
protocol,
|
||||
protocol_info,
|
||||
protocol_query,
|
||||
protocol_request,
|
||||
proxy_authenticate,
|
||||
proxy_authentication_info,
|
||||
proxy_authorization,
|
||||
proxy_connection,
|
||||
proxy_features,
|
||||
proxy_instruction,
|
||||
public_,
|
||||
public_key_pins,
|
||||
public_key_pins_report_only,
|
||||
range,
|
||||
received,
|
||||
received_spf,
|
||||
redirect_ref,
|
||||
references,
|
||||
referer,
|
||||
referer_root,
|
||||
relay_version,
|
||||
reply_by,
|
||||
reply_to,
|
||||
require_recipient_valid_since,
|
||||
resent_bcc,
|
||||
resent_cc,
|
||||
resent_date,
|
||||
resent_from,
|
||||
resent_message_id,
|
||||
resent_reply_to,
|
||||
resent_sender,
|
||||
resent_to,
|
||||
resolution_hint,
|
||||
resolver_location,
|
||||
retry_after,
|
||||
return_path,
|
||||
safe,
|
||||
schedule_reply,
|
||||
schedule_tag,
|
||||
sec_fetch_dest,
|
||||
sec_fetch_mode,
|
||||
sec_fetch_site,
|
||||
sec_fetch_user,
|
||||
sec_websocket_accept,
|
||||
sec_websocket_extensions,
|
||||
sec_websocket_key,
|
||||
sec_websocket_protocol,
|
||||
sec_websocket_version,
|
||||
security_scheme,
|
||||
see_also,
|
||||
sender,
|
||||
sensitivity,
|
||||
server,
|
||||
set_cookie,
|
||||
set_cookie2,
|
||||
setprofile,
|
||||
sio_label,
|
||||
sio_label_history,
|
||||
slug,
|
||||
soapaction,
|
||||
solicitation,
|
||||
status_uri,
|
||||
strict_transport_security,
|
||||
subject,
|
||||
subok,
|
||||
subst,
|
||||
summary,
|
||||
supersedes,
|
||||
surrogate_capability,
|
||||
surrogate_control,
|
||||
tcn,
|
||||
te,
|
||||
timeout,
|
||||
title,
|
||||
to,
|
||||
topic,
|
||||
trailer,
|
||||
transfer_encoding,
|
||||
ttl,
|
||||
ua_color,
|
||||
ua_media,
|
||||
ua_pixels,
|
||||
ua_resolution,
|
||||
ua_windowpixels,
|
||||
upgrade,
|
||||
urgency,
|
||||
uri,
|
||||
user_agent,
|
||||
variant_vary,
|
||||
vary,
|
||||
vbr_info,
|
||||
version,
|
||||
via,
|
||||
want_digest,
|
||||
warning,
|
||||
www_authenticate,
|
||||
x_archived_at,
|
||||
x_device_accept,
|
||||
x_device_accept_charset,
|
||||
x_device_accept_encoding,
|
||||
x_device_accept_language,
|
||||
x_device_user_agent,
|
||||
x_frame_options,
|
||||
x_mittente,
|
||||
x_pgp_sig,
|
||||
x_ricevuta,
|
||||
x_riferimento_message_id,
|
||||
x_tiporicevuta,
|
||||
x_trasporto,
|
||||
x_verificasicurezza,
|
||||
x400_content_identifier,
|
||||
x400_content_return,
|
||||
x400_content_type,
|
||||
x400_mts_identifier,
|
||||
x400_originator,
|
||||
x400_received,
|
||||
x400_recipients,
|
||||
x400_trace,
|
||||
xref
|
||||
};
|
||||
/** Convert a field enum to a string.
|
||||
|
||||
@param f The field to convert
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
string_view
|
||||
to_string(field f);
|
||||
|
||||
/** Attempt to convert a string to a field enum.
|
||||
|
||||
The string comparison is case-insensitive.
|
||||
|
||||
@return The corresponding field, or @ref field::unknown
|
||||
if no known field matches.
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
field
|
||||
string_to_field(string_view s);
|
||||
|
||||
/// Write the text for a field name to an output stream.
|
||||
BOOST_BEAST_DECL
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, field f);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#ifdef BOOST_BEAST_HEADER_ONLY
|
||||
#include <boost/beast/http/impl/field.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+798
@@ -0,0 +1,798 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_FIELDS_HPP
|
||||
#define BOOST_BEAST_HTTP_FIELDS_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/detail/allocator.hpp>
|
||||
#include <boost/beast/http/field.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/core/empty_value.hpp>
|
||||
#include <boost/intrusive/list.hpp>
|
||||
#include <boost/intrusive/set.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A container for storing HTTP header fields.
|
||||
|
||||
This container is designed to store the field value pairs that make
|
||||
up the fields and trailers in an HTTP message. Objects of this type
|
||||
are iterable, with each element holding the field name and field
|
||||
value.
|
||||
|
||||
Field names are stored as-is, but comparisons are case-insensitive.
|
||||
The container behaves as a `std::multiset`; there will be a separate
|
||||
value for each occurrence of the same field name. When the container
|
||||
is iterated the fields are presented in the order of insertion, with
|
||||
fields having the same name following each other consecutively.
|
||||
|
||||
Meets the requirements of <em>Fields</em>
|
||||
|
||||
@tparam Allocator The allocator to use.
|
||||
*/
|
||||
template<class Allocator>
|
||||
class basic_fields
|
||||
#if ! BOOST_BEAST_DOXYGEN
|
||||
: private boost::empty_value<Allocator>
|
||||
#endif
|
||||
{
|
||||
// Fancy pointers are not supported
|
||||
static_assert(std::is_pointer<typename
|
||||
std::allocator_traits<Allocator>::pointer>::value,
|
||||
"Allocator must use regular pointers");
|
||||
|
||||
#ifndef BOOST_BEAST_DOXYGEN
|
||||
friend class fields_test; // for `header`
|
||||
#endif
|
||||
|
||||
struct element;
|
||||
|
||||
using off_t = std::uint16_t;
|
||||
|
||||
public:
|
||||
/// The type of allocator used.
|
||||
using allocator_type = Allocator;
|
||||
|
||||
/// The type of element used to represent a field
|
||||
class value_type
|
||||
{
|
||||
#ifndef BOOST_BEAST_DOXYGEN
|
||||
friend class basic_fields;
|
||||
#endif
|
||||
|
||||
off_t off_;
|
||||
off_t len_;
|
||||
field f_;
|
||||
|
||||
char*
|
||||
data() const;
|
||||
|
||||
net::const_buffer
|
||||
buffer() const;
|
||||
|
||||
protected:
|
||||
value_type(field name,
|
||||
string_view sname, string_view value);
|
||||
|
||||
public:
|
||||
/// Constructor (deleted)
|
||||
value_type(value_type const&) = delete;
|
||||
|
||||
/// Assignment (deleted)
|
||||
value_type& operator=(value_type const&) = delete;
|
||||
|
||||
/// Returns the field enum, which can be @ref boost::beast::http::field::unknown
|
||||
field
|
||||
name() const;
|
||||
|
||||
/// Returns the field name as a string
|
||||
string_view const
|
||||
name_string() const;
|
||||
|
||||
/// Returns the value of the field
|
||||
string_view const
|
||||
value() const;
|
||||
};
|
||||
|
||||
/** A strictly less predicate for comparing keys, using a case-insensitive comparison.
|
||||
|
||||
The case-comparison operation is defined only for low-ASCII characters.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using key_compare = __implementation_defined__;
|
||||
#else
|
||||
struct key_compare : beast::iless
|
||||
#endif
|
||||
{
|
||||
/// Returns `true` if lhs is less than rhs using a strict ordering
|
||||
bool
|
||||
operator()(
|
||||
string_view lhs,
|
||||
value_type const& rhs) const noexcept
|
||||
{
|
||||
if(lhs.size() < rhs.name_string().size())
|
||||
return true;
|
||||
if(lhs.size() > rhs.name_string().size())
|
||||
return false;
|
||||
return iless::operator()(lhs, rhs.name_string());
|
||||
}
|
||||
|
||||
/// Returns `true` if lhs is less than rhs using a strict ordering
|
||||
bool
|
||||
operator()(
|
||||
value_type const& lhs,
|
||||
string_view rhs) const noexcept
|
||||
{
|
||||
if(lhs.name_string().size() < rhs.size())
|
||||
return true;
|
||||
if(lhs.name_string().size() > rhs.size())
|
||||
return false;
|
||||
return iless::operator()(lhs.name_string(), rhs);
|
||||
}
|
||||
|
||||
/// Returns `true` if lhs is less than rhs using a strict ordering
|
||||
bool
|
||||
operator()(
|
||||
value_type const& lhs,
|
||||
value_type const& rhs) const noexcept
|
||||
{
|
||||
if(lhs.name_string().size() < rhs.name_string().size())
|
||||
return true;
|
||||
if(lhs.name_string().size() > rhs.name_string().size())
|
||||
return false;
|
||||
return iless::operator()(lhs.name_string(), rhs.name_string());
|
||||
}
|
||||
};
|
||||
|
||||
/// The algorithm used to serialize the header
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using writer = __implementation_defined__;
|
||||
#else
|
||||
class writer;
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct element
|
||||
: public boost::intrusive::list_base_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>
|
||||
, public boost::intrusive::set_base_hook<
|
||||
boost::intrusive::link_mode<
|
||||
boost::intrusive::normal_link>>
|
||||
, public value_type
|
||||
{
|
||||
element(field name,
|
||||
string_view sname, string_view value);
|
||||
};
|
||||
|
||||
using list_t = typename boost::intrusive::make_list<
|
||||
element,
|
||||
boost::intrusive::constant_time_size<false>
|
||||
>::type;
|
||||
|
||||
using set_t = typename boost::intrusive::make_multiset<
|
||||
element,
|
||||
boost::intrusive::constant_time_size<false>,
|
||||
boost::intrusive::compare<key_compare>
|
||||
>::type;
|
||||
|
||||
using align_type = typename
|
||||
boost::type_with_alignment<alignof(element)>::type;
|
||||
|
||||
using rebind_type = typename
|
||||
beast::detail::allocator_traits<Allocator>::
|
||||
template rebind_alloc<align_type>;
|
||||
|
||||
using alloc_traits =
|
||||
beast::detail::allocator_traits<rebind_type>;
|
||||
|
||||
using size_type = typename
|
||||
beast::detail::allocator_traits<Allocator>::size_type;
|
||||
|
||||
|
||||
public:
|
||||
/// Destructor
|
||||
~basic_fields();
|
||||
|
||||
/// Constructor.
|
||||
basic_fields() = default;
|
||||
|
||||
/** Constructor.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
explicit
|
||||
basic_fields(Allocator const& alloc) noexcept;
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The state of the moved-from object is
|
||||
as if constructed using the same allocator.
|
||||
*/
|
||||
basic_fields(basic_fields&&) noexcept;
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
The state of the moved-from object is
|
||||
as if constructed using the same allocator.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_fields(basic_fields&&, Allocator const& alloc);
|
||||
|
||||
/// Copy constructor.
|
||||
basic_fields(basic_fields const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
basic_fields(basic_fields const&, Allocator const& alloc);
|
||||
|
||||
/// Copy constructor.
|
||||
template<class OtherAlloc>
|
||||
basic_fields(basic_fields<OtherAlloc> const&);
|
||||
|
||||
/** Copy constructor.
|
||||
|
||||
@param alloc The allocator to use.
|
||||
*/
|
||||
template<class OtherAlloc>
|
||||
basic_fields(basic_fields<OtherAlloc> const&,
|
||||
Allocator const& alloc);
|
||||
|
||||
/** Move assignment.
|
||||
|
||||
The state of the moved-from object is
|
||||
as if constructed using the same allocator.
|
||||
*/
|
||||
basic_fields& operator=(basic_fields&&) noexcept(
|
||||
alloc_traits::propagate_on_container_move_assignment::value);
|
||||
|
||||
/// Copy assignment.
|
||||
basic_fields& operator=(basic_fields const&);
|
||||
|
||||
/// Copy assignment.
|
||||
template<class OtherAlloc>
|
||||
basic_fields& operator=(basic_fields<OtherAlloc> const&);
|
||||
|
||||
public:
|
||||
/// A constant iterator to the field sequence.
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = __implementation_defined__;
|
||||
#else
|
||||
using const_iterator = typename list_t::const_iterator;
|
||||
#endif
|
||||
|
||||
/// A constant iterator to the field sequence.
|
||||
using iterator = const_iterator;
|
||||
|
||||
/// Return a copy of the allocator associated with the container.
|
||||
allocator_type
|
||||
get_allocator() const
|
||||
{
|
||||
return this->get();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Element access
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Returns the value for a field, or throws an exception.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The name of the field.
|
||||
|
||||
@return The field value.
|
||||
|
||||
@throws std::out_of_range if the field is not found.
|
||||
*/
|
||||
string_view const
|
||||
at(field name) const;
|
||||
|
||||
/** Returns the value for a field, or throws an exception.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The name of the field. It is interpreted as a case-insensitive string.
|
||||
|
||||
@return The field value.
|
||||
|
||||
@throws std::out_of_range if the field is not found.
|
||||
*/
|
||||
string_view const
|
||||
at(string_view name) const;
|
||||
|
||||
/** Returns the value for a field, or `""` if it does not exist.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The name of the field.
|
||||
*/
|
||||
string_view const
|
||||
operator[](field name) const;
|
||||
|
||||
/** Returns the value for a case-insensitive matching header, or `""` if it does not exist.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The name of the field. It is interpreted as a case-insensitive string.
|
||||
*/
|
||||
string_view const
|
||||
operator[](string_view name) const;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Iterators
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Return a const iterator to the beginning of the field sequence.
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
/// Return a const iterator to the end of the field sequence.
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
/// Return a const iterator to the beginning of the field sequence.
|
||||
const_iterator
|
||||
cbegin() const
|
||||
{
|
||||
return list_.cbegin();
|
||||
}
|
||||
|
||||
/// Return a const iterator to the end of the field sequence.
|
||||
const_iterator
|
||||
cend() const
|
||||
{
|
||||
return list_.cend();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Capacity
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
// VFALCO Since the header and message derive from Fields,
|
||||
// what does the expression m.empty() mean? Its confusing.
|
||||
bool
|
||||
empty() const
|
||||
{
|
||||
return list_.empty();
|
||||
}
|
||||
public:
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Modifiers
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Remove all fields from the container
|
||||
|
||||
All references, pointers, or iterators referring to contained
|
||||
elements are invalidated. All past-the-end iterators are also
|
||||
invalidated.
|
||||
|
||||
@par Postconditions:
|
||||
@code
|
||||
std::distance(this->begin(), this->end()) == 0
|
||||
@endcode
|
||||
*/
|
||||
void
|
||||
clear();
|
||||
|
||||
/** Insert a field.
|
||||
|
||||
If one or more fields with the same name already exist,
|
||||
the new field will be inserted after the last field with
|
||||
the matching name, in serialization order.
|
||||
The value can be an empty string.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@param value The value of the field, as a @ref boost::beast::string_view
|
||||
*/
|
||||
void
|
||||
insert(field name, string_view const& value);
|
||||
|
||||
/* Set a field from a null pointer (deleted).
|
||||
*/
|
||||
void
|
||||
insert(field, std::nullptr_t) = delete;
|
||||
|
||||
/** Insert a field.
|
||||
|
||||
If one or more fields with the same name already exist,
|
||||
the new field will be inserted after the last field with
|
||||
the matching name, in serialization order.
|
||||
The value can be an empty string.
|
||||
|
||||
@param name The field name. It is interpreted as a case-insensitive string.
|
||||
|
||||
@param value The value of the field, as a @ref boost::beast::string_view
|
||||
*/
|
||||
void
|
||||
insert(string_view name, string_view const& value);
|
||||
|
||||
/* Insert a field from a null pointer (deleted).
|
||||
*/
|
||||
void
|
||||
insert(string_view, std::nullptr_t) = delete;
|
||||
|
||||
/** Insert a field.
|
||||
|
||||
If one or more fields with the same name already exist,
|
||||
the new field will be inserted after the last field with
|
||||
the matching name, in serialization order.
|
||||
The value can be an empty string.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@param name_string The literal text corresponding to the
|
||||
field name. If `name != field::unknown`, then this value
|
||||
must be equal to `to_string(name)` using a case-insensitive
|
||||
comparison, otherwise the behavior is undefined.
|
||||
|
||||
@param value The value of the field, as a @ref boost::beast::string_view
|
||||
*/
|
||||
void
|
||||
insert(field name, string_view name_string,
|
||||
string_view const& value);
|
||||
|
||||
void
|
||||
insert(field, string_view, std::nullptr_t) = delete;
|
||||
|
||||
/** Set a field value, removing any other instances of that field.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value. The value may be an empty string.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@param value The value of the field, as a @ref boost::beast::string_view
|
||||
|
||||
@return The field value.
|
||||
|
||||
*/
|
||||
void
|
||||
set(field name, string_view const& value);
|
||||
|
||||
void
|
||||
set(field, std::nullptr_t) = delete;
|
||||
|
||||
/** Set a field value, removing any other instances of that field.
|
||||
|
||||
First removes any values with matching field names, then
|
||||
inserts the new field value. The value can be an empty string.
|
||||
|
||||
@param name The field name. It is interpreted as a case-insensitive string.
|
||||
|
||||
@param value The value of the field, as a @ref boost::beast::string_view
|
||||
*/
|
||||
void
|
||||
set(string_view name, string_view const& value);
|
||||
|
||||
void
|
||||
set(string_view, std::nullptr_t) = delete;
|
||||
|
||||
/** Remove a field.
|
||||
|
||||
References and iterators to the erased elements are
|
||||
invalidated. Other references and iterators are not
|
||||
affected.
|
||||
|
||||
@param pos An iterator to the element to remove.
|
||||
|
||||
@return An iterator following the last removed element.
|
||||
If the iterator refers to the last element, the end()
|
||||
iterator is returned.
|
||||
*/
|
||||
const_iterator
|
||||
erase(const_iterator pos);
|
||||
|
||||
/** Remove all fields with the specified name.
|
||||
|
||||
All fields with the same field name are erased from the
|
||||
container.
|
||||
References and iterators to the erased elements are
|
||||
invalidated. Other references and iterators are not
|
||||
affected.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return The number of fields removed.
|
||||
*/
|
||||
std::size_t
|
||||
erase(field name);
|
||||
|
||||
/** Remove all fields with the specified name.
|
||||
|
||||
All fields with the same field name are erased from the
|
||||
container.
|
||||
References and iterators to the erased elements are
|
||||
invalidated. Other references and iterators are not
|
||||
affected.
|
||||
|
||||
@param name The field name. It is interpreted as a case-insensitive string.
|
||||
|
||||
@return The number of fields removed.
|
||||
*/
|
||||
std::size_t
|
||||
erase(string_view name);
|
||||
|
||||
/** Return a buffer sequence representing the trailers.
|
||||
|
||||
This function returns a buffer sequence holding the
|
||||
serialized representation of the trailer fields promised
|
||||
in the Accept field. Before calling this function the
|
||||
Accept field must contain the exact trailer fields
|
||||
desired. Each field must also exist.
|
||||
*/
|
||||
|
||||
|
||||
/// Swap this container with another
|
||||
void
|
||||
swap(basic_fields& other);
|
||||
|
||||
/// Swap two field containers
|
||||
template<class Alloc>
|
||||
friend
|
||||
void
|
||||
swap(basic_fields<Alloc>& lhs, basic_fields<Alloc>& rhs);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Lookup
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/** Return the number of fields with the specified name.
|
||||
|
||||
@param name The field name.
|
||||
*/
|
||||
std::size_t
|
||||
count(field name) const;
|
||||
|
||||
/** Return the number of fields with the specified name.
|
||||
|
||||
@param name The field name. It is interpreted as a case-insensitive string.
|
||||
*/
|
||||
std::size_t
|
||||
count(string_view name) const;
|
||||
|
||||
/** Returns an iterator to the case-insensitive matching field.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return An iterator to the matching field, or `end()` if
|
||||
no match was found.
|
||||
*/
|
||||
const_iterator
|
||||
find(field name) const;
|
||||
|
||||
/** Returns an iterator to the case-insensitive matching field name.
|
||||
|
||||
If more than one field with the specified name exists, the
|
||||
first field defined by insertion order is returned.
|
||||
|
||||
@param name The field name. It is interpreted as a case-insensitive string.
|
||||
|
||||
@return An iterator to the matching field, or `end()` if
|
||||
no match was found.
|
||||
*/
|
||||
const_iterator
|
||||
find(string_view name) const;
|
||||
|
||||
/** Returns a range of iterators to the fields with the specified name.
|
||||
|
||||
This function returns the first and last iterators to the ordered
|
||||
fields with the specified name.
|
||||
|
||||
@note The fields represented by the range are ordered. Its elements
|
||||
are guaranteed to match the field ordering of the message. This
|
||||
means users do not need to sort this range when comparing fields
|
||||
of the same name in different messages.
|
||||
|
||||
@param name The field name.
|
||||
|
||||
@return A range of iterators to fields with the same name,
|
||||
otherwise an empty range.
|
||||
*/
|
||||
std::pair<const_iterator, const_iterator>
|
||||
equal_range(field name) const;
|
||||
|
||||
/// @copydoc boost::beast::http::basic_fields::equal_range(boost::beast::http::field) const
|
||||
std::pair<const_iterator, const_iterator>
|
||||
equal_range(string_view name) const;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Observers
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/// Returns a copy of the key comparison function
|
||||
key_compare
|
||||
key_comp() const
|
||||
{
|
||||
return key_compare{};
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Returns the request-method string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
string_view
|
||||
get_method_impl() const;
|
||||
|
||||
/** Returns the request-target string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
string_view
|
||||
get_target_impl() const;
|
||||
|
||||
/** Returns the response reason-phrase string.
|
||||
|
||||
@note Only called for responses.
|
||||
*/
|
||||
string_view
|
||||
get_reason_impl() const;
|
||||
|
||||
/** Returns the chunked Transfer-Encoding setting
|
||||
*/
|
||||
bool
|
||||
get_chunked_impl() const;
|
||||
|
||||
/** Returns the keep-alive setting
|
||||
*/
|
||||
bool
|
||||
get_keep_alive_impl(unsigned version) const;
|
||||
|
||||
/** Returns `true` if the Content-Length field is present.
|
||||
*/
|
||||
bool
|
||||
has_content_length_impl() const;
|
||||
|
||||
/** Set or clear the method string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void
|
||||
set_method_impl(string_view s);
|
||||
|
||||
/** Set or clear the target string.
|
||||
|
||||
@note Only called for requests.
|
||||
*/
|
||||
void
|
||||
set_target_impl(string_view s);
|
||||
|
||||
/** Set or clear the reason string.
|
||||
|
||||
@note Only called for responses.
|
||||
*/
|
||||
void
|
||||
set_reason_impl(string_view s);
|
||||
|
||||
/** Adjusts the chunked Transfer-Encoding value
|
||||
*/
|
||||
void
|
||||
set_chunked_impl(bool value);
|
||||
|
||||
/** Sets or clears the Content-Length field
|
||||
*/
|
||||
void
|
||||
set_content_length_impl(
|
||||
boost::optional<std::uint64_t> const& value);
|
||||
|
||||
/** Adjusts the Connection field
|
||||
*/
|
||||
void
|
||||
set_keep_alive_impl(
|
||||
unsigned version, bool keep_alive);
|
||||
|
||||
private:
|
||||
template<class OtherAlloc>
|
||||
friend class basic_fields;
|
||||
|
||||
element&
|
||||
new_element(field name,
|
||||
string_view sname, string_view value);
|
||||
|
||||
void
|
||||
delete_element(element& e);
|
||||
|
||||
void
|
||||
set_element(element& e);
|
||||
|
||||
void
|
||||
realloc_string(string_view& dest, string_view s);
|
||||
|
||||
void
|
||||
realloc_target(
|
||||
string_view& dest, string_view s);
|
||||
|
||||
template<class OtherAlloc>
|
||||
void
|
||||
copy_all(basic_fields<OtherAlloc> const&);
|
||||
|
||||
void
|
||||
clear_all();
|
||||
|
||||
void
|
||||
delete_list();
|
||||
|
||||
void
|
||||
move_assign(basic_fields&, std::true_type);
|
||||
|
||||
void
|
||||
move_assign(basic_fields&, std::false_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_fields const&, std::true_type);
|
||||
|
||||
void
|
||||
copy_assign(basic_fields const&, std::false_type);
|
||||
|
||||
void
|
||||
swap(basic_fields& other, std::true_type);
|
||||
|
||||
void
|
||||
swap(basic_fields& other, std::false_type);
|
||||
|
||||
set_t set_;
|
||||
list_t list_;
|
||||
string_view method_;
|
||||
string_view target_or_reason_;
|
||||
};
|
||||
|
||||
/// A typical HTTP header fields container
|
||||
using fields = basic_fields<std::allocator<char>>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/fields.hpp>
|
||||
|
||||
#endif
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_FILE_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_FILE_BODY_HPP
|
||||
|
||||
#include <boost/beast/core/file.hpp>
|
||||
#include <boost/beast/http/basic_file_body.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/// A message body represented by a file on the filesystem.
|
||||
using file_body = basic_file_body<file>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#ifndef BOOST_BEAST_NO_FILE_BODY_WIN32
|
||||
#include <boost/beast/http/impl/file_body_win32.hpp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_HPP
|
||||
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/make_unique.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_parser<isRequest>::
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(net::is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence type requirements not met");
|
||||
auto const p = net::buffer_sequence_begin(buffers);
|
||||
auto const last = net::buffer_sequence_end(buffers);
|
||||
if(p == last)
|
||||
{
|
||||
ec = {};
|
||||
return 0;
|
||||
}
|
||||
if(std::next(p) == last)
|
||||
{
|
||||
// single buffer
|
||||
return put(net::const_buffer(*p), ec);
|
||||
}
|
||||
auto const size = buffer_bytes(buffers);
|
||||
if(size <= max_stack_buffer)
|
||||
return put_from_stack(size, buffers, ec);
|
||||
if(size > buf_len_)
|
||||
{
|
||||
// reallocate
|
||||
buf_ = boost::make_unique_noinit<char[]>(size);
|
||||
buf_len_ = size;
|
||||
}
|
||||
// flatten
|
||||
net::buffer_copy(net::buffer(
|
||||
buf_.get(), size), buffers);
|
||||
return put(net::const_buffer{
|
||||
buf_.get(), size}, ec);
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
boost::optional<std::uint64_t>
|
||||
basic_parser<isRequest>::
|
||||
content_length_unchecked() const
|
||||
{
|
||||
if(f_ & flagContentLength)
|
||||
return len0_;
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
basic_parser<isRequest>::
|
||||
put_from_stack(std::size_t size,
|
||||
ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
char buf[max_stack_buffer];
|
||||
net::buffer_copy(net::mutable_buffer(
|
||||
buf, sizeof(buf)), buffers);
|
||||
return put(net::const_buffer{
|
||||
buf, size}, ec);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+913
@@ -0,0 +1,913 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP
|
||||
|
||||
#include <boost/beast/http/basic_parser.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/rfc7230.hpp>
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#include <boost/beast/core/detail/clamp.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/detail/string.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest>
|
||||
bool
|
||||
basic_parser<isRequest>::
|
||||
keep_alive() const
|
||||
{
|
||||
BOOST_ASSERT(is_header_done());
|
||||
if(f_ & flagHTTP11)
|
||||
{
|
||||
if(f_ & flagConnectionClose)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(! (f_ & flagConnectionKeepAlive))
|
||||
return false;
|
||||
}
|
||||
return (f_ & flagNeedEOF) == 0;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
boost::optional<std::uint64_t>
|
||||
basic_parser<isRequest>::
|
||||
content_length() const
|
||||
{
|
||||
BOOST_ASSERT(is_header_done());
|
||||
return content_length_unchecked();
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
boost::optional<std::uint64_t>
|
||||
basic_parser<isRequest>::
|
||||
content_length_remaining() const
|
||||
{
|
||||
BOOST_ASSERT(is_header_done());
|
||||
if(! (f_ & flagContentLength))
|
||||
return boost::none;
|
||||
return len_;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
skip(bool v)
|
||||
{
|
||||
BOOST_ASSERT(! got_some());
|
||||
if(v)
|
||||
f_ |= flagSkipBody;
|
||||
else
|
||||
f_ &= ~flagSkipBody;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
std::size_t
|
||||
basic_parser<isRequest>::
|
||||
put(net::const_buffer buffer,
|
||||
error_code& ec)
|
||||
{
|
||||
// If this goes off you have tried to parse more data after the parser
|
||||
// has completed. A common cause of this is re-using a parser, which is
|
||||
// not supported. If you need to re-use a parser, consider storing it
|
||||
// in an optional. Then reset() and emplace() prior to parsing each new
|
||||
// message.
|
||||
BOOST_ASSERT(!is_done());
|
||||
if (is_done())
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::stale_parser);
|
||||
return 0;
|
||||
}
|
||||
auto p = static_cast<char const*>(buffer.data());
|
||||
auto n = buffer.size();
|
||||
auto const p0 = p;
|
||||
auto const p1 = p0 + n;
|
||||
ec = {};
|
||||
loop:
|
||||
switch(state_)
|
||||
{
|
||||
case state::nothing_yet:
|
||||
if(n == 0)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return 0;
|
||||
}
|
||||
state_ = state::start_line;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
case state::start_line:
|
||||
{
|
||||
maybe_need_more(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
parse_start_line(p, p + (std::min<std::size_t>)(
|
||||
header_limit_, n), ec, is_request{});
|
||||
if(ec)
|
||||
{
|
||||
if(ec == error::need_more)
|
||||
{
|
||||
if(n >= header_limit_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
|
||||
goto done;
|
||||
}
|
||||
if(p + 3 <= p1)
|
||||
skip_ = static_cast<
|
||||
std::size_t>(p1 - p - 3);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
BOOST_ASSERT(! is_done());
|
||||
n = static_cast<std::size_t>(p1 - p);
|
||||
if(p >= p1)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
goto done;
|
||||
}
|
||||
BOOST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case state::fields:
|
||||
maybe_need_more(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
parse_fields(p, p + (std::min<std::size_t>)(
|
||||
header_limit_, n), ec);
|
||||
if(ec)
|
||||
{
|
||||
if(ec == error::need_more)
|
||||
{
|
||||
if(n >= header_limit_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
|
||||
goto done;
|
||||
}
|
||||
if(p + 3 <= p1)
|
||||
skip_ = static_cast<
|
||||
std::size_t>(p1 - p - 3);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
finish_header(ec, is_request{});
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::body0:
|
||||
BOOST_ASSERT(! skip_);
|
||||
this->on_body_init_impl(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::body;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
case state::body:
|
||||
BOOST_ASSERT(! skip_);
|
||||
parse_body(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::body_to_eof0:
|
||||
BOOST_ASSERT(! skip_);
|
||||
this->on_body_init_impl(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::body_to_eof;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
case state::body_to_eof:
|
||||
BOOST_ASSERT(! skip_);
|
||||
parse_body_to_eof(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::chunk_header0:
|
||||
this->on_body_init_impl(content_length(), ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
state_ = state::chunk_header;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
case state::chunk_header:
|
||||
parse_chunk_header(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::chunk_body:
|
||||
parse_chunk_body(p, n, ec);
|
||||
if(ec)
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case state::complete:
|
||||
ec = {};
|
||||
goto done;
|
||||
}
|
||||
if(p < p1 && ! is_done() && eager())
|
||||
{
|
||||
n = static_cast<std::size_t>(p1 - p);
|
||||
goto loop;
|
||||
}
|
||||
done:
|
||||
return static_cast<std::size_t>(p - p0);
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
put_eof(error_code& ec)
|
||||
{
|
||||
BOOST_ASSERT(got_some());
|
||||
if( state_ == state::start_line ||
|
||||
state_ == state::fields)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::partial_message);
|
||||
return;
|
||||
}
|
||||
if(f_ & (flagContentLength | flagChunked))
|
||||
{
|
||||
if(state_ != state::complete)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::partial_message);
|
||||
return;
|
||||
}
|
||||
ec = {};
|
||||
return;
|
||||
}
|
||||
ec = {};
|
||||
this->on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
maybe_need_more(
|
||||
char const* p, std::size_t n,
|
||||
error_code& ec)
|
||||
{
|
||||
if(skip_ == 0)
|
||||
return;
|
||||
if( n > header_limit_)
|
||||
n = header_limit_;
|
||||
if(n < skip_ + 4)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
auto const term =
|
||||
find_eom(p + skip_, p + n);
|
||||
if(! term)
|
||||
{
|
||||
skip_ = n - 3;
|
||||
if(skip_ + 4 > header_limit_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::header_limit);
|
||||
return;
|
||||
}
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
skip_ = 0;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
parse_start_line(
|
||||
char const*& in, char const* last,
|
||||
error_code& ec, std::true_type)
|
||||
{
|
||||
/*
|
||||
request-line = method SP request-target SP HTTP-version CRLF
|
||||
method = token
|
||||
*/
|
||||
auto p = in;
|
||||
|
||||
string_view method;
|
||||
parse_method(p, last, method, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
string_view target;
|
||||
parse_target(p, last, target, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
int version = 0;
|
||||
parse_version(p, last, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(version < 10 || version > 11)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
|
||||
if(p + 2 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(p[0] != '\r' || p[1] != '\n')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
p += 2;
|
||||
|
||||
if(version >= 11)
|
||||
f_ |= flagHTTP11;
|
||||
|
||||
this->on_request_impl(string_to_verb(method),
|
||||
method, target, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
in = p;
|
||||
state_ = state::fields;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
parse_start_line(
|
||||
char const*& in, char const* last,
|
||||
error_code& ec, std::false_type)
|
||||
{
|
||||
/*
|
||||
status-line = HTTP-version SP status-code SP reason-phrase CRLF
|
||||
status-code = 3*DIGIT
|
||||
reason-phrase = *( HTAB / SP / VCHAR / obs-text )
|
||||
*/
|
||||
auto p = in;
|
||||
|
||||
int version = 0;
|
||||
parse_version(p, last, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(version < 10 || version > 11)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
|
||||
// SP
|
||||
if(p + 1 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(*p++ != ' ')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_version);
|
||||
return;
|
||||
}
|
||||
|
||||
parse_status(p, last, status_, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
// parse reason CRLF
|
||||
string_view reason;
|
||||
parse_reason(p, last, reason, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
if(version >= 11)
|
||||
f_ |= flagHTTP11;
|
||||
|
||||
this->on_response_impl(
|
||||
status_, reason, version, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
in = p;
|
||||
state_ = state::fields;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
parse_fields(char const*& in,
|
||||
char const* last, error_code& ec)
|
||||
{
|
||||
string_view name;
|
||||
string_view value;
|
||||
// https://stackoverflow.com/questions/686217/maximum-on-http-header-values
|
||||
beast::detail::char_buffer<max_obs_fold> buf;
|
||||
auto p = in;
|
||||
for(;;)
|
||||
{
|
||||
if(p + 2 > last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(p[0] == '\r')
|
||||
{
|
||||
if(p[1] != '\n')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_line_ending);
|
||||
}
|
||||
in = p + 2;
|
||||
return;
|
||||
}
|
||||
parse_field(p, last, name, value, buf, ec);
|
||||
if(ec)
|
||||
return;
|
||||
auto const f = string_to_field(name);
|
||||
do_field(f, value, ec);
|
||||
if(ec)
|
||||
return;
|
||||
this->on_field_impl(f, name, value, ec);
|
||||
if(ec)
|
||||
return;
|
||||
in = p;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
finish_header(error_code& ec, std::true_type)
|
||||
{
|
||||
// RFC 7230 section 3.3
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.3
|
||||
|
||||
if(f_ & flagSkipBody)
|
||||
{
|
||||
state_ = state::complete;
|
||||
}
|
||||
else if(f_ & flagContentLength)
|
||||
{
|
||||
if(body_limit_.has_value() &&
|
||||
len_ > body_limit_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
|
||||
return;
|
||||
}
|
||||
if(len_ > 0)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::body0;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = state::complete;
|
||||
}
|
||||
}
|
||||
else if(f_ & flagChunked)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::chunk_header0;
|
||||
}
|
||||
else
|
||||
{
|
||||
len_ = 0;
|
||||
len0_ = 0;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
ec = {};
|
||||
this->on_header_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(state_ == state::complete)
|
||||
{
|
||||
this->on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
finish_header(error_code& ec, std::false_type)
|
||||
{
|
||||
// RFC 7230 section 3.3
|
||||
// https://tools.ietf.org/html/rfc7230#section-3.3
|
||||
|
||||
if( (f_ & flagSkipBody) || // e.g. response to a HEAD request
|
||||
status_ / 100 == 1 || // 1xx e.g. Continue
|
||||
status_ == 204 || // No Content
|
||||
status_ == 304) // Not Modified
|
||||
{
|
||||
// VFALCO Content-Length may be present, but we
|
||||
// treat the message as not having a body.
|
||||
// https://github.com/boostorg/beast/issues/692
|
||||
state_ = state::complete;
|
||||
}
|
||||
else if(f_ & flagContentLength)
|
||||
{
|
||||
if(len_ > 0)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::body0;
|
||||
|
||||
if(body_limit_.has_value() &&
|
||||
len_ > body_limit_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = state::complete;
|
||||
}
|
||||
}
|
||||
else if(f_ & flagChunked)
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
state_ = state::chunk_header0;
|
||||
}
|
||||
else
|
||||
{
|
||||
f_ |= flagHasBody;
|
||||
f_ |= flagNeedEOF;
|
||||
state_ = state::body_to_eof0;
|
||||
}
|
||||
|
||||
ec = {};
|
||||
this->on_header_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(state_ == state::complete)
|
||||
{
|
||||
this->on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
parse_body(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
n = this->on_body_impl(string_view{p,
|
||||
beast::detail::clamp(len_, n)}, ec);
|
||||
p += n;
|
||||
len_ -= n;
|
||||
if(ec)
|
||||
return;
|
||||
if(len_ > 0)
|
||||
return;
|
||||
this->on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
parse_body_to_eof(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
if(body_limit_.has_value())
|
||||
{
|
||||
if (n > *body_limit_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
|
||||
return;
|
||||
}
|
||||
*body_limit_ -= n;
|
||||
}
|
||||
ec = {};
|
||||
n = this->on_body_impl(string_view{p, n}, ec);
|
||||
p += n;
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
parse_chunk_header(char const*& p0,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
/*
|
||||
chunked-body = *chunk last-chunk trailer-part CRLF
|
||||
|
||||
chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
|
||||
last-chunk = 1*("0") [ chunk-ext ] CRLF
|
||||
trailer-part = *( header-field CRLF )
|
||||
|
||||
chunk-size = 1*HEXDIG
|
||||
chunk-data = 1*OCTET ; a sequence of chunk-size octets
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
*/
|
||||
|
||||
auto p = p0;
|
||||
auto const pend = p + n;
|
||||
char const* eol;
|
||||
|
||||
if(! (f_ & flagFinalChunk))
|
||||
{
|
||||
if(n < skip_ + 2)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
if(f_ & flagExpectCRLF)
|
||||
{
|
||||
// Treat the last CRLF in a chunk as
|
||||
// part of the next chunk, so p can
|
||||
// be parsed in one call instead of two.
|
||||
if(! parse_crlf(p))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
eol = find_eol(p0 + skip_, pend, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! eol)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
skip_ = n - 1;
|
||||
return;
|
||||
}
|
||||
skip_ = static_cast<
|
||||
std::size_t>(eol - 2 - p0);
|
||||
|
||||
std::uint64_t size;
|
||||
if(! parse_hex(p, size))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk);
|
||||
return;
|
||||
}
|
||||
if(size != 0)
|
||||
{
|
||||
if (body_limit_.has_value())
|
||||
{
|
||||
if (size > *body_limit_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::body_limit);
|
||||
return;
|
||||
}
|
||||
*body_limit_ -= size;
|
||||
}
|
||||
auto const start = p;
|
||||
parse_chunk_extensions(p, pend, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(p != eol -2 )
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return;
|
||||
}
|
||||
auto const ext = make_string(start, p);
|
||||
this->on_chunk_header_impl(size, ext, ec);
|
||||
if(ec)
|
||||
return;
|
||||
len_ = size;
|
||||
skip_ = 2;
|
||||
p0 = eol;
|
||||
f_ |= flagExpectCRLF;
|
||||
state_ = state::chunk_body;
|
||||
return;
|
||||
}
|
||||
|
||||
f_ |= flagFinalChunk;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(n >= 5);
|
||||
if(f_ & flagExpectCRLF)
|
||||
BOOST_VERIFY(parse_crlf(p));
|
||||
std::uint64_t size;
|
||||
BOOST_VERIFY(parse_hex(p, size));
|
||||
eol = find_eol(p, pend, ec);
|
||||
BOOST_ASSERT(! ec);
|
||||
}
|
||||
|
||||
auto eom = find_eom(p0 + skip_, pend);
|
||||
if(! eom)
|
||||
{
|
||||
BOOST_ASSERT(n >= 3);
|
||||
skip_ = n - 3;
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::need_more);
|
||||
return;
|
||||
}
|
||||
|
||||
auto const start = p;
|
||||
parse_chunk_extensions(p, pend, ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(p != eol - 2)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return;
|
||||
}
|
||||
auto const ext = make_string(start, p);
|
||||
this->on_chunk_header_impl(0, ext, ec);
|
||||
if(ec)
|
||||
return;
|
||||
p = eol;
|
||||
parse_fields(p, eom, ec);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(p == eom);
|
||||
p0 = eom;
|
||||
|
||||
this->on_finish_impl(ec);
|
||||
if(ec)
|
||||
return;
|
||||
state_ = state::complete;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
parse_chunk_body(char const*& p,
|
||||
std::size_t n, error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
n = this->on_chunk_body_impl(
|
||||
len_, string_view{p,
|
||||
beast::detail::clamp(len_, n)}, ec);
|
||||
p += n;
|
||||
len_ -= n;
|
||||
if(len_ == 0)
|
||||
state_ = state::chunk_header;
|
||||
}
|
||||
|
||||
template<bool isRequest>
|
||||
void
|
||||
basic_parser<isRequest>::
|
||||
do_field(field f,
|
||||
string_view value, error_code& ec)
|
||||
{
|
||||
using namespace beast::detail::string_literals;
|
||||
// Connection
|
||||
if(f == field::connection ||
|
||||
f == field::proxy_connection)
|
||||
{
|
||||
auto const list = opt_token_list{value};
|
||||
if(! validate_list(list))
|
||||
{
|
||||
// VFALCO Should this be a field specific error?
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_value);
|
||||
return;
|
||||
}
|
||||
for(auto const& s : list)
|
||||
{
|
||||
if(beast::iequals("close"_sv, s))
|
||||
{
|
||||
f_ |= flagConnectionClose;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(beast::iequals("keep-alive"_sv, s))
|
||||
{
|
||||
f_ |= flagConnectionKeepAlive;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(beast::iequals("upgrade"_sv, s))
|
||||
{
|
||||
f_ |= flagConnectionUpgrade;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ec = {};
|
||||
return;
|
||||
}
|
||||
|
||||
// Content-Length
|
||||
if(f == field::content_length)
|
||||
{
|
||||
auto bad_content_length = [&ec]
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_content_length);
|
||||
};
|
||||
|
||||
auto multiple_content_length = [&ec]
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::multiple_content_length);
|
||||
};
|
||||
|
||||
// conflicting field
|
||||
if(f_ & flagChunked)
|
||||
return bad_content_length();
|
||||
|
||||
// Content-length may be a comma-separated list of integers
|
||||
auto tokens_unprocessed = 1 +
|
||||
std::count(value.begin(), value.end(), ',');
|
||||
auto tokens = opt_token_list(value);
|
||||
if (tokens.begin() == tokens.end() ||
|
||||
!validate_list(tokens))
|
||||
return bad_content_length();
|
||||
|
||||
auto existing = this->content_length_unchecked();
|
||||
for (auto tok : tokens)
|
||||
{
|
||||
std::uint64_t v;
|
||||
if (!parse_dec(tok, v))
|
||||
return bad_content_length();
|
||||
--tokens_unprocessed;
|
||||
if (existing.has_value())
|
||||
{
|
||||
if (v != *existing)
|
||||
return multiple_content_length();
|
||||
}
|
||||
else
|
||||
{
|
||||
existing = v;
|
||||
}
|
||||
}
|
||||
|
||||
if (tokens_unprocessed)
|
||||
return bad_content_length();
|
||||
|
||||
BOOST_ASSERT(existing.has_value());
|
||||
ec = {};
|
||||
len_ = *existing;
|
||||
len0_ = *existing;
|
||||
f_ |= flagContentLength;
|
||||
return;
|
||||
}
|
||||
|
||||
// Transfer-Encoding
|
||||
if(f == field::transfer_encoding)
|
||||
{
|
||||
if(f_ & flagChunked)
|
||||
{
|
||||
// duplicate
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_transfer_encoding);
|
||||
return;
|
||||
}
|
||||
|
||||
if(f_ & flagContentLength)
|
||||
{
|
||||
// conflicting field
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_transfer_encoding);
|
||||
return;
|
||||
}
|
||||
|
||||
ec = {};
|
||||
auto const v = token_list{value};
|
||||
auto const p = std::find_if(v.begin(), v.end(),
|
||||
[&](string_view const& s)
|
||||
{
|
||||
return beast::iequals("chunked"_sv, s);
|
||||
});
|
||||
if(p == v.end())
|
||||
return;
|
||||
if(std::next(p) != v.end())
|
||||
return;
|
||||
len_ = 0;
|
||||
f_ |= flagChunked;
|
||||
return;
|
||||
}
|
||||
|
||||
// Upgrade
|
||||
if(f == field::upgrade)
|
||||
{
|
||||
ec = {};
|
||||
f_ |= flagUpgrade;
|
||||
return;
|
||||
}
|
||||
|
||||
ec = {};
|
||||
}
|
||||
|
||||
#ifdef BOOST_BEAST_SOURCE
|
||||
template class http::basic_parser<true>;
|
||||
template class http::basic_parser<false>;
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+706
@@ -0,0 +1,706 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_CHUNK_ENCODE_HPP
|
||||
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#include <boost/beast/core/detail/varint.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/detail/rfc7230.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
inline
|
||||
chunk_header::
|
||||
chunk_header(std::size_t size)
|
||||
: view_(
|
||||
size,
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{})
|
||||
{
|
||||
BOOST_ASSERT(size > 0);
|
||||
}
|
||||
|
||||
inline
|
||||
chunk_header::
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
string_view extensions)
|
||||
: view_(
|
||||
size,
|
||||
net::const_buffer{
|
||||
extensions.data(), extensions.size()},
|
||||
chunk_crlf{})
|
||||
{
|
||||
BOOST_ASSERT(size > 0);
|
||||
}
|
||||
|
||||
template<class ChunkExtensions, class>
|
||||
chunk_header::
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
ChunkExtensions&& extensions)
|
||||
: exts_(std::make_shared<detail::chunk_extensions_impl<
|
||||
typename std::decay<ChunkExtensions>::type>>(
|
||||
std::forward<ChunkExtensions>(extensions)))
|
||||
, view_(
|
||||
size,
|
||||
exts_->str(),
|
||||
chunk_crlf{})
|
||||
{
|
||||
static_assert(
|
||||
detail::is_chunk_extensions<ChunkExtensions>::value,
|
||||
"ChunkExtensions requirements not met");
|
||||
BOOST_ASSERT(size > 0);
|
||||
}
|
||||
|
||||
template<class ChunkExtensions, class Allocator, class>
|
||||
chunk_header::
|
||||
chunk_header(
|
||||
std::size_t size,
|
||||
ChunkExtensions&& extensions,
|
||||
Allocator const& allocator)
|
||||
: exts_(std::allocate_shared<detail::chunk_extensions_impl<
|
||||
typename std::decay<ChunkExtensions>::type>>(allocator,
|
||||
std::forward<ChunkExtensions>(extensions)))
|
||||
, view_(
|
||||
size,
|
||||
exts_->str(),
|
||||
chunk_crlf{})
|
||||
{
|
||||
static_assert(
|
||||
detail::is_chunk_extensions<ChunkExtensions>::value,
|
||||
"ChunkExtensions requirements not met");
|
||||
BOOST_ASSERT(size > 0);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
chunk_body<ConstBufferSequence>::
|
||||
chunk_body(ConstBufferSequence const& buffers)
|
||||
: view_(
|
||||
buffer_bytes(buffers),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
buffers,
|
||||
chunk_crlf{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
chunk_body<ConstBufferSequence>::
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
string_view extensions)
|
||||
: view_(
|
||||
buffer_bytes(buffers),
|
||||
net::const_buffer{
|
||||
extensions.data(), extensions.size()},
|
||||
chunk_crlf{},
|
||||
buffers,
|
||||
chunk_crlf{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
template<class ChunkExtensions, class>
|
||||
chunk_body<ConstBufferSequence>::
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
ChunkExtensions&& extensions)
|
||||
: exts_(std::make_shared<detail::chunk_extensions_impl<
|
||||
typename std::decay<ChunkExtensions>::type>>(
|
||||
std::forward<ChunkExtensions>(extensions)))
|
||||
, view_(
|
||||
buffer_bytes(buffers),
|
||||
exts_->str(),
|
||||
chunk_crlf{},
|
||||
buffers,
|
||||
chunk_crlf{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
template<class ChunkExtensions, class Allocator, class>
|
||||
chunk_body<ConstBufferSequence>::
|
||||
chunk_body(
|
||||
ConstBufferSequence const& buffers,
|
||||
ChunkExtensions&& extensions,
|
||||
Allocator const& allocator)
|
||||
: exts_(std::allocate_shared<detail::chunk_extensions_impl<
|
||||
typename std::decay<ChunkExtensions>::type>>(allocator,
|
||||
std::forward<ChunkExtensions>(extensions)))
|
||||
, view_(
|
||||
buffer_bytes(buffers),
|
||||
exts_->str(),
|
||||
chunk_crlf{},
|
||||
buffers,
|
||||
chunk_crlf{})
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Trailer>
|
||||
template<class Allocator>
|
||||
auto
|
||||
chunk_last<Trailer>::
|
||||
prepare(Trailer const& trailer, Allocator const& allocator) ->
|
||||
buffers_type
|
||||
{
|
||||
auto sp = std::allocate_shared<typename
|
||||
Trailer::writer>(allocator, trailer);
|
||||
sp_ = sp;
|
||||
return sp->get();
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
auto
|
||||
chunk_last<Trailer>::
|
||||
prepare(Trailer const& trailer, std::true_type) ->
|
||||
buffers_type
|
||||
{
|
||||
auto sp = std::make_shared<
|
||||
typename Trailer::writer>(trailer);
|
||||
sp_ = sp;
|
||||
return sp->get();
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
auto
|
||||
chunk_last<Trailer>::
|
||||
prepare(Trailer const& trailer, std::false_type) ->
|
||||
buffers_type
|
||||
{
|
||||
return trailer;
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
chunk_last<Trailer>::
|
||||
chunk_last()
|
||||
: view_(
|
||||
detail::chunk_size0{},
|
||||
Trailer{})
|
||||
{
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
chunk_last<Trailer>::
|
||||
chunk_last(Trailer const& trailer)
|
||||
: view_(
|
||||
detail::chunk_size0{},
|
||||
prepare(trailer, is_fields<Trailer>{}))
|
||||
{
|
||||
}
|
||||
|
||||
template<class Trailer>
|
||||
template<class DeducedTrailer, class Allocator, class>
|
||||
chunk_last<Trailer>::
|
||||
chunk_last(
|
||||
DeducedTrailer const& trailer, Allocator const& allocator)
|
||||
: view_(
|
||||
detail::chunk_size0{},
|
||||
prepare(trailer, allocator))
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
class basic_chunk_extensions<Allocator>::const_iterator
|
||||
{
|
||||
friend class basic_chunk_extensions;
|
||||
|
||||
using iter_type = char const*;
|
||||
|
||||
iter_type it_;
|
||||
typename basic_chunk_extensions::value_type value_;
|
||||
|
||||
explicit
|
||||
const_iterator(iter_type it)
|
||||
: it_(it)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
increment();
|
||||
|
||||
public:
|
||||
using value_type = typename
|
||||
basic_chunk_extensions::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::forward_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator&& other) = default;
|
||||
const_iterator(const_iterator const& other) = default;
|
||||
const_iterator& operator=(const_iterator&& other) = default;
|
||||
const_iterator& operator=(const_iterator const& other) = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return it_ == other.it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*();
|
||||
|
||||
pointer
|
||||
operator->()
|
||||
{
|
||||
return &(**this);
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
increment();
|
||||
return temp;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
const_iterator::
|
||||
increment()
|
||||
{
|
||||
using beast::detail::varint_read;
|
||||
auto n = varint_read(it_);
|
||||
it_ += n;
|
||||
n = varint_read(it_);
|
||||
it_ += n;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_chunk_extensions<Allocator>::
|
||||
const_iterator::
|
||||
operator*() ->
|
||||
reference
|
||||
{
|
||||
using beast::detail::varint_read;
|
||||
auto it = it_;
|
||||
auto n = varint_read(it);
|
||||
value_.first = string_view{it, n};
|
||||
it += n;
|
||||
n = varint_read(it);
|
||||
value_.second = string_view{it, n};
|
||||
return value_;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Allocator>
|
||||
template<class FwdIt>
|
||||
FwdIt
|
||||
basic_chunk_extensions<Allocator>::
|
||||
do_parse(FwdIt it, FwdIt last, error_code& ec)
|
||||
{
|
||||
/*
|
||||
chunk-ext = *( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
|
||||
BWS = *( SP / HTAB ) ; "Bad White Space"
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
token = 1*tchar
|
||||
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
obs-text = %x80-FF
|
||||
|
||||
https://www.rfc-editor.org/errata_search.php?rfc=7230&eid=4667
|
||||
*/
|
||||
using beast::detail::varint_size;
|
||||
using beast::detail::varint_write;
|
||||
using CharT = char;
|
||||
using Traits = std::char_traits<CharT>;
|
||||
range_.reserve(static_cast<std::size_t>(
|
||||
std::distance(it, last) * 1.2));
|
||||
range_.resize(0);
|
||||
auto const emit_string =
|
||||
[this](FwdIt from, FwdIt to)
|
||||
{
|
||||
auto const len =
|
||||
std::distance(from, to);
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(len) +
|
||||
len);
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, len);
|
||||
Traits::copy(dest, from, len);
|
||||
};
|
||||
auto const emit_string_plus_empty =
|
||||
[this](FwdIt from, FwdIt to)
|
||||
{
|
||||
auto const len =
|
||||
std::distance(from, to);
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(len) +
|
||||
len +
|
||||
varint_size(0));
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, len);
|
||||
Traits::copy(dest, from, len);
|
||||
dest += len;
|
||||
varint_write(dest, 0);
|
||||
};
|
||||
auto const emit_empty_string =
|
||||
[this]
|
||||
{
|
||||
auto const offset = range_.size();
|
||||
range_.resize(offset + varint_size(0));
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, 0);
|
||||
};
|
||||
loop:
|
||||
if(it == last)
|
||||
{
|
||||
ec = {};
|
||||
return it;
|
||||
}
|
||||
// BWS
|
||||
if(*it == ' ' || *it == '\t')
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
}
|
||||
}
|
||||
// ';'
|
||||
if(*it != ';')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
semi:
|
||||
++it; // skip ';'
|
||||
// BWS
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
// chunk-ext-name
|
||||
{
|
||||
if(! detail::is_token_char(*it))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
auto const first = it;
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
emit_string_plus_empty(first, it);
|
||||
return it;
|
||||
}
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
emit_string(first, it);
|
||||
}
|
||||
// BWS [ ";" / "=" ]
|
||||
for(;;)
|
||||
{
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
}
|
||||
if(*it == ';')
|
||||
{
|
||||
emit_empty_string();
|
||||
goto semi;
|
||||
}
|
||||
if(*it != '=')
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
++it; // skip '='
|
||||
// BWS
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
// chunk-ext-val
|
||||
if(*it != '"')
|
||||
{
|
||||
// token
|
||||
if(! detail::is_token_char(*it))
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
auto const first = it;
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
break;
|
||||
if(! detail::is_token_char(*it))
|
||||
break;
|
||||
}
|
||||
emit_string(first, it);
|
||||
if(it == last)
|
||||
return it;
|
||||
}
|
||||
else
|
||||
{
|
||||
// quoted-string
|
||||
auto const first = ++it; // skip DQUOTE
|
||||
// first pass, count chars
|
||||
std::size_t len = 0;
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
if(*it == '"')
|
||||
break;
|
||||
if(*it == '\\')
|
||||
{
|
||||
++it;
|
||||
if(it == last)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::bad_chunk_extension);
|
||||
return it;
|
||||
}
|
||||
}
|
||||
++len;
|
||||
++it;
|
||||
}
|
||||
// now build the string
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(len) +
|
||||
len);
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, len);
|
||||
it = first;
|
||||
for(;;)
|
||||
{
|
||||
BOOST_ASSERT(it != last);
|
||||
if(*it == '"')
|
||||
break;
|
||||
if(*it == '\\')
|
||||
{
|
||||
++it;
|
||||
BOOST_ASSERT(it != last);
|
||||
}
|
||||
Traits::assign(*dest++, *it++);
|
||||
}
|
||||
++it; // skip DQUOTE
|
||||
}
|
||||
goto loop;
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
do_insert(string_view name, string_view value)
|
||||
{
|
||||
/*
|
||||
chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
|
||||
chunk-ext-name = token
|
||||
chunk-ext-val = token / quoted-string
|
||||
token = 1*tchar
|
||||
quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||
qdtext = HTAB / SP / "!" / %x23-5B ; '#'-'[' / %x5D-7E ; ']'-'~' / obs-text
|
||||
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
obs-text = %x80-FF
|
||||
*/
|
||||
if(value.empty())
|
||||
{
|
||||
s_.reserve(1 + name.size());
|
||||
s_.push_back(';');
|
||||
s_.append(name.data(), name.size());
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_token = true;
|
||||
for(auto const c : value)
|
||||
{
|
||||
if(! detail::is_token_char(c))
|
||||
{
|
||||
is_token = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(is_token)
|
||||
{
|
||||
// token
|
||||
s_.reserve(1 + name.size() + 1 + value.size());
|
||||
s_.push_back(';');
|
||||
s_.append(name.data(), name.size());
|
||||
s_.push_back('=');
|
||||
s_.append(value.data(), value.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// quoted-string
|
||||
s_.reserve(
|
||||
1 + name.size() + 1 +
|
||||
1 + value.size() + 20 + 1);
|
||||
s_.push_back(';');
|
||||
s_.append(name.data(), name.size());
|
||||
s_.append("=\"", 2);
|
||||
for(auto const c : value)
|
||||
{
|
||||
if(c == '\\')
|
||||
s_.append(R"(\\)", 2);
|
||||
else if(c == '\"')
|
||||
s_.append(R"(\")", 2);
|
||||
else
|
||||
s_.push_back(c);
|
||||
}
|
||||
s_.push_back('"');
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
parse(string_view s, error_code& ec)
|
||||
{
|
||||
do_parse(s.data(), s.data() + s.size(), ec);
|
||||
if(! ec)
|
||||
{
|
||||
s_.clear();
|
||||
for(auto const& v : *this)
|
||||
do_insert(v.first, v.second);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
insert(string_view name)
|
||||
{
|
||||
do_insert(name, {});
|
||||
|
||||
using beast::detail::varint_size;
|
||||
using beast::detail::varint_write;
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(name.size()) +
|
||||
name.size() +
|
||||
varint_size(0));
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, name.size());
|
||||
std::memcpy(dest, name.data(), name.size());
|
||||
dest += name.size();
|
||||
varint_write(dest, 0);
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
void
|
||||
basic_chunk_extensions<Allocator>::
|
||||
insert(string_view name, string_view value)
|
||||
{
|
||||
do_insert(name, value);
|
||||
|
||||
using beast::detail::varint_size;
|
||||
using beast::detail::varint_write;
|
||||
auto const offset = range_.size();
|
||||
range_.resize(
|
||||
offset +
|
||||
varint_size(name.size()) +
|
||||
name.size() +
|
||||
varint_size(value.size()) +
|
||||
value.size());
|
||||
auto dest = &range_[offset];
|
||||
varint_write(dest, name.size());
|
||||
std::memcpy(dest, name.data(), name.size());
|
||||
dest += name.size();
|
||||
varint_write(dest, value.size());
|
||||
std::memcpy(dest, value.data(), value.size());
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_chunk_extensions<Allocator>::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{range_.data()};
|
||||
}
|
||||
|
||||
template<class Allocator>
|
||||
auto
|
||||
basic_chunk_extensions<Allocator>::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{
|
||||
range_.data() + range_.size()};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_ERROR_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_ERROR_HPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
template<>
|
||||
struct is_error_code_enum<::boost::beast::http::error>
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // system
|
||||
} // boost
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
error_code
|
||||
make_error_code(error ev);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_ERROR_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_ERROR_IPP
|
||||
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
class http_error_category : public error_category
|
||||
{
|
||||
public:
|
||||
const char*
|
||||
name() const noexcept override
|
||||
{
|
||||
return "beast.http";
|
||||
}
|
||||
|
||||
http_error_category() : error_category(0x964627da815bf210u) {}
|
||||
|
||||
std::string
|
||||
message(int ev) const override
|
||||
{
|
||||
switch(static_cast<error>(ev))
|
||||
{
|
||||
case error::end_of_stream: return "end of stream";
|
||||
case error::partial_message: return "partial message";
|
||||
case error::need_more: return "need more";
|
||||
case error::unexpected_body: return "unexpected body";
|
||||
case error::need_buffer: return "need buffer";
|
||||
case error::end_of_chunk: return "end of chunk";
|
||||
case error::buffer_overflow: return "buffer overflow";
|
||||
case error::header_limit: return "header limit exceeded";
|
||||
case error::body_limit: return "body limit exceeded";
|
||||
case error::bad_alloc: return "bad alloc";
|
||||
case error::bad_line_ending: return "bad line ending";
|
||||
case error::bad_method: return "bad method";
|
||||
case error::bad_target: return "bad target";
|
||||
case error::bad_version: return "bad version";
|
||||
case error::bad_status: return "bad status";
|
||||
case error::bad_reason: return "bad reason";
|
||||
case error::bad_field: return "bad field";
|
||||
case error::bad_value: return "bad value";
|
||||
case error::bad_content_length: return "bad Content-Length";
|
||||
case error::bad_transfer_encoding: return "bad Transfer-Encoding";
|
||||
case error::bad_chunk: return "bad chunk";
|
||||
case error::bad_chunk_extension: return "bad chunk extension";
|
||||
case error::bad_obs_fold: return "bad obs-fold";
|
||||
case error::multiple_content_length: return "multiple Content-Length";
|
||||
case error::stale_parser: return "stale parser";
|
||||
case error::short_read: return "unexpected eof in body";
|
||||
|
||||
default:
|
||||
return "beast.http error";
|
||||
}
|
||||
}
|
||||
|
||||
error_condition
|
||||
default_error_condition(
|
||||
int ev) const noexcept override
|
||||
{
|
||||
return error_condition{ev, *this};
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(int ev,
|
||||
error_condition const& condition
|
||||
) const noexcept override
|
||||
{
|
||||
return condition.value() == ev &&
|
||||
&condition.category() == this;
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(error_code const& error,
|
||||
int ev) const noexcept override
|
||||
{
|
||||
return error.value() == ev &&
|
||||
&error.category() == this;
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
error_code
|
||||
make_error_code(error ev)
|
||||
{
|
||||
static detail::http_error_category const cat{};
|
||||
return error_code{static_cast<
|
||||
std::underlying_type<error>::type>(ev), cat};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+586
@@ -0,0 +1,586 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_FIELD_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_FIELD_IPP
|
||||
|
||||
#include <boost/beast/http/field.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <ostream>
|
||||
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct field_table
|
||||
{
|
||||
static
|
||||
std::uint32_t
|
||||
get_chars(
|
||||
unsigned char const* p) noexcept
|
||||
{
|
||||
// VFALCO memcpy is endian-dependent
|
||||
//std::memcpy(&v, p, 4);
|
||||
// Compiler should be smart enough to
|
||||
// optimize this down to one instruction.
|
||||
return
|
||||
p[0] |
|
||||
(p[1] << 8) |
|
||||
(p[2] << 16) |
|
||||
(p[3] << 24);
|
||||
}
|
||||
|
||||
using array_type =
|
||||
std::array<string_view, 357>;
|
||||
|
||||
// Strings are converted to lowercase
|
||||
static
|
||||
std::uint32_t
|
||||
digest(string_view s)
|
||||
{
|
||||
std::uint32_t r = 0;
|
||||
std::size_t n = s.size();
|
||||
auto p = reinterpret_cast<
|
||||
unsigned char const*>(s.data());
|
||||
// consume N characters at a time
|
||||
// VFALCO Can we do 8 on 64-bit systems?
|
||||
while(n >= 4)
|
||||
{
|
||||
auto const v = get_chars(p);
|
||||
r = (r * 5 + (
|
||||
v | 0x20202020 )); // convert to lower
|
||||
p += 4;
|
||||
n -= 4;
|
||||
}
|
||||
// handle remaining characters
|
||||
while( n > 0 )
|
||||
{
|
||||
r = r * 5 + ( *p | 0x20 );
|
||||
++p;
|
||||
--n;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// This comparison is case-insensitive, and the
|
||||
// strings must contain only valid http field characters.
|
||||
static
|
||||
bool
|
||||
equals(string_view lhs, string_view rhs)
|
||||
{
|
||||
using Int = std::uint32_t; // VFALCO std::size_t?
|
||||
auto n = lhs.size();
|
||||
if(n != rhs.size())
|
||||
return false;
|
||||
auto p1 = reinterpret_cast<
|
||||
unsigned char const*>(lhs.data());
|
||||
auto p2 = reinterpret_cast<
|
||||
unsigned char const*>(rhs.data());
|
||||
auto constexpr S = sizeof(Int);
|
||||
auto constexpr Mask = static_cast<Int>(
|
||||
0xDFDFDFDFDFDFDFDF & ~Int{0});
|
||||
for(; n >= S; p1 += S, p2 += S, n -= S)
|
||||
{
|
||||
Int const v1 = get_chars(p1);
|
||||
Int const v2 = get_chars(p2);
|
||||
if((v1 ^ v2) & Mask)
|
||||
return false;
|
||||
}
|
||||
for(; n; ++p1, ++p2, --n)
|
||||
if(( *p1 ^ *p2) & 0xDF)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
array_type by_name_;
|
||||
|
||||
enum { N = 5155 };
|
||||
unsigned char map_[ N ][ 2 ] = {};
|
||||
|
||||
/*
|
||||
From:
|
||||
|
||||
https://www.iana.org/assignments/message-headers/message-headers.xhtml
|
||||
*/
|
||||
field_table()
|
||||
: by_name_({{
|
||||
// string constants
|
||||
"<unknown-field>",
|
||||
"A-IM",
|
||||
"Accept",
|
||||
"Accept-Additions",
|
||||
"Accept-Charset",
|
||||
"Accept-Datetime",
|
||||
"Accept-Encoding",
|
||||
"Accept-Features",
|
||||
"Accept-Language",
|
||||
"Accept-Patch",
|
||||
"Accept-Post",
|
||||
"Accept-Ranges",
|
||||
"Access-Control",
|
||||
"Access-Control-Allow-Credentials",
|
||||
"Access-Control-Allow-Headers",
|
||||
"Access-Control-Allow-Methods",
|
||||
"Access-Control-Allow-Origin",
|
||||
"Access-Control-Expose-Headers",
|
||||
"Access-Control-Max-Age",
|
||||
"Access-Control-Request-Headers",
|
||||
"Access-Control-Request-Method",
|
||||
"Age",
|
||||
"Allow",
|
||||
"ALPN",
|
||||
"Also-Control",
|
||||
"Alt-Svc",
|
||||
"Alt-Used",
|
||||
"Alternate-Recipient",
|
||||
"Alternates",
|
||||
"Apparently-To",
|
||||
"Apply-To-Redirect-Ref",
|
||||
"Approved",
|
||||
"Archive",
|
||||
"Archived-At",
|
||||
"Article-Names",
|
||||
"Article-Updates",
|
||||
"Authentication-Control",
|
||||
"Authentication-Info",
|
||||
"Authentication-Results",
|
||||
"Authorization",
|
||||
"Auto-Submitted",
|
||||
"Autoforwarded",
|
||||
"Autosubmitted",
|
||||
"Base",
|
||||
"Bcc",
|
||||
"Body",
|
||||
"C-Ext",
|
||||
"C-Man",
|
||||
"C-Opt",
|
||||
"C-PEP",
|
||||
"C-PEP-Info",
|
||||
"Cache-Control",
|
||||
"CalDAV-Timezones",
|
||||
"Cancel-Key",
|
||||
"Cancel-Lock",
|
||||
"Cc",
|
||||
"Close",
|
||||
"Comments",
|
||||
"Compliance",
|
||||
"Connection",
|
||||
"Content-Alternative",
|
||||
"Content-Base",
|
||||
"Content-Description",
|
||||
"Content-Disposition",
|
||||
"Content-Duration",
|
||||
"Content-Encoding",
|
||||
"Content-features",
|
||||
"Content-ID",
|
||||
"Content-Identifier",
|
||||
"Content-Language",
|
||||
"Content-Length",
|
||||
"Content-Location",
|
||||
"Content-MD5",
|
||||
"Content-Range",
|
||||
"Content-Return",
|
||||
"Content-Script-Type",
|
||||
"Content-Style-Type",
|
||||
"Content-Transfer-Encoding",
|
||||
"Content-Type",
|
||||
"Content-Version",
|
||||
"Control",
|
||||
"Conversion",
|
||||
"Conversion-With-Loss",
|
||||
"Cookie",
|
||||
"Cookie2",
|
||||
"Cost",
|
||||
"DASL",
|
||||
"Date",
|
||||
"Date-Received",
|
||||
"DAV",
|
||||
"Default-Style",
|
||||
"Deferred-Delivery",
|
||||
"Delivery-Date",
|
||||
"Delta-Base",
|
||||
"Depth",
|
||||
"Derived-From",
|
||||
"Destination",
|
||||
"Differential-ID",
|
||||
"Digest",
|
||||
"Discarded-X400-IPMS-Extensions",
|
||||
"Discarded-X400-MTS-Extensions",
|
||||
"Disclose-Recipients",
|
||||
"Disposition-Notification-Options",
|
||||
"Disposition-Notification-To",
|
||||
"Distribution",
|
||||
"DKIM-Signature",
|
||||
"DL-Expansion-History",
|
||||
"Downgraded-Bcc",
|
||||
"Downgraded-Cc",
|
||||
"Downgraded-Disposition-Notification-To",
|
||||
"Downgraded-Final-Recipient",
|
||||
"Downgraded-From",
|
||||
"Downgraded-In-Reply-To",
|
||||
"Downgraded-Mail-From",
|
||||
"Downgraded-Message-Id",
|
||||
"Downgraded-Original-Recipient",
|
||||
"Downgraded-Rcpt-To",
|
||||
"Downgraded-References",
|
||||
"Downgraded-Reply-To",
|
||||
"Downgraded-Resent-Bcc",
|
||||
"Downgraded-Resent-Cc",
|
||||
"Downgraded-Resent-From",
|
||||
"Downgraded-Resent-Reply-To",
|
||||
"Downgraded-Resent-Sender",
|
||||
"Downgraded-Resent-To",
|
||||
"Downgraded-Return-Path",
|
||||
"Downgraded-Sender",
|
||||
"Downgraded-To",
|
||||
"EDIINT-Features",
|
||||
"Eesst-Version",
|
||||
"Encoding",
|
||||
"Encrypted",
|
||||
"Errors-To",
|
||||
"ETag",
|
||||
"Expect",
|
||||
"Expires",
|
||||
"Expiry-Date",
|
||||
"Ext",
|
||||
"Followup-To",
|
||||
"Forwarded",
|
||||
"From",
|
||||
"Generate-Delivery-Report",
|
||||
"GetProfile",
|
||||
"Hobareg",
|
||||
"Host",
|
||||
"HTTP2-Settings",
|
||||
"If",
|
||||
"If-Match",
|
||||
"If-Modified-Since",
|
||||
"If-None-Match",
|
||||
"If-Range",
|
||||
"If-Schedule-Tag-Match",
|
||||
"If-Unmodified-Since",
|
||||
"IM",
|
||||
"Importance",
|
||||
"In-Reply-To",
|
||||
"Incomplete-Copy",
|
||||
"Injection-Date",
|
||||
"Injection-Info",
|
||||
"Jabber-ID",
|
||||
"Keep-Alive",
|
||||
"Keywords",
|
||||
"Label",
|
||||
"Language",
|
||||
"Last-Modified",
|
||||
"Latest-Delivery-Time",
|
||||
"Lines",
|
||||
"Link",
|
||||
"List-Archive",
|
||||
"List-Help",
|
||||
"List-ID",
|
||||
"List-Owner",
|
||||
"List-Post",
|
||||
"List-Subscribe",
|
||||
"List-Unsubscribe",
|
||||
"List-Unsubscribe-Post",
|
||||
"Location",
|
||||
"Lock-Token",
|
||||
"Man",
|
||||
"Max-Forwards",
|
||||
"Memento-Datetime",
|
||||
"Message-Context",
|
||||
"Message-ID",
|
||||
"Message-Type",
|
||||
"Meter",
|
||||
"Method-Check",
|
||||
"Method-Check-Expires",
|
||||
"MIME-Version",
|
||||
"MMHS-Acp127-Message-Identifier",
|
||||
"MMHS-Authorizing-Users",
|
||||
"MMHS-Codress-Message-Indicator",
|
||||
"MMHS-Copy-Precedence",
|
||||
"MMHS-Exempted-Address",
|
||||
"MMHS-Extended-Authorisation-Info",
|
||||
"MMHS-Handling-Instructions",
|
||||
"MMHS-Message-Instructions",
|
||||
"MMHS-Message-Type",
|
||||
"MMHS-Originator-PLAD",
|
||||
"MMHS-Originator-Reference",
|
||||
"MMHS-Other-Recipients-Indicator-CC",
|
||||
"MMHS-Other-Recipients-Indicator-To",
|
||||
"MMHS-Primary-Precedence",
|
||||
"MMHS-Subject-Indicator-Codes",
|
||||
"MT-Priority",
|
||||
"Negotiate",
|
||||
"Newsgroups",
|
||||
"NNTP-Posting-Date",
|
||||
"NNTP-Posting-Host",
|
||||
"Non-Compliance",
|
||||
"Obsoletes",
|
||||
"Opt",
|
||||
"Optional",
|
||||
"Optional-WWW-Authenticate",
|
||||
"Ordering-Type",
|
||||
"Organization",
|
||||
"Origin",
|
||||
"Original-Encoded-Information-Types",
|
||||
"Original-From",
|
||||
"Original-Message-ID",
|
||||
"Original-Recipient",
|
||||
"Original-Sender",
|
||||
"Original-Subject",
|
||||
"Originator-Return-Address",
|
||||
"Overwrite",
|
||||
"P3P",
|
||||
"Path",
|
||||
"PEP",
|
||||
"Pep-Info",
|
||||
"PICS-Label",
|
||||
"Position",
|
||||
"Posting-Version",
|
||||
"Pragma",
|
||||
"Prefer",
|
||||
"Preference-Applied",
|
||||
"Prevent-NonDelivery-Report",
|
||||
"Priority",
|
||||
"Privicon",
|
||||
"ProfileObject",
|
||||
"Protocol",
|
||||
"Protocol-Info",
|
||||
"Protocol-Query",
|
||||
"Protocol-Request",
|
||||
"Proxy-Authenticate",
|
||||
"Proxy-Authentication-Info",
|
||||
"Proxy-Authorization",
|
||||
"Proxy-Connection",
|
||||
"Proxy-Features",
|
||||
"Proxy-Instruction",
|
||||
"Public",
|
||||
"Public-Key-Pins",
|
||||
"Public-Key-Pins-Report-Only",
|
||||
"Range",
|
||||
"Received",
|
||||
"Received-SPF",
|
||||
"Redirect-Ref",
|
||||
"References",
|
||||
"Referer",
|
||||
"Referer-Root",
|
||||
"Relay-Version",
|
||||
"Reply-By",
|
||||
"Reply-To",
|
||||
"Require-Recipient-Valid-Since",
|
||||
"Resent-Bcc",
|
||||
"Resent-Cc",
|
||||
"Resent-Date",
|
||||
"Resent-From",
|
||||
"Resent-Message-ID",
|
||||
"Resent-Reply-To",
|
||||
"Resent-Sender",
|
||||
"Resent-To",
|
||||
"Resolution-Hint",
|
||||
"Resolver-Location",
|
||||
"Retry-After",
|
||||
"Return-Path",
|
||||
"Safe",
|
||||
"Schedule-Reply",
|
||||
"Schedule-Tag",
|
||||
"Sec-Fetch-Dest",
|
||||
"Sec-Fetch-Mode",
|
||||
"Sec-Fetch-Site",
|
||||
"Sec-Fetch-User",
|
||||
"Sec-WebSocket-Accept",
|
||||
"Sec-WebSocket-Extensions",
|
||||
"Sec-WebSocket-Key",
|
||||
"Sec-WebSocket-Protocol",
|
||||
"Sec-WebSocket-Version",
|
||||
"Security-Scheme",
|
||||
"See-Also",
|
||||
"Sender",
|
||||
"Sensitivity",
|
||||
"Server",
|
||||
"Set-Cookie",
|
||||
"Set-Cookie2",
|
||||
"SetProfile",
|
||||
"SIO-Label",
|
||||
"SIO-Label-History",
|
||||
"SLUG",
|
||||
"SoapAction",
|
||||
"Solicitation",
|
||||
"Status-URI",
|
||||
"Strict-Transport-Security",
|
||||
"Subject",
|
||||
"SubOK",
|
||||
"Subst",
|
||||
"Summary",
|
||||
"Supersedes",
|
||||
"Surrogate-Capability",
|
||||
"Surrogate-Control",
|
||||
"TCN",
|
||||
"TE",
|
||||
"Timeout",
|
||||
"Title",
|
||||
"To",
|
||||
"Topic",
|
||||
"Trailer",
|
||||
"Transfer-Encoding",
|
||||
"TTL",
|
||||
"UA-Color",
|
||||
"UA-Media",
|
||||
"UA-Pixels",
|
||||
"UA-Resolution",
|
||||
"UA-Windowpixels",
|
||||
"Upgrade",
|
||||
"Urgency",
|
||||
"URI",
|
||||
"User-Agent",
|
||||
"Variant-Vary",
|
||||
"Vary",
|
||||
"VBR-Info",
|
||||
"Version",
|
||||
"Via",
|
||||
"Want-Digest",
|
||||
"Warning",
|
||||
"WWW-Authenticate",
|
||||
"X-Archived-At",
|
||||
"X-Device-Accept",
|
||||
"X-Device-Accept-Charset",
|
||||
"X-Device-Accept-Encoding",
|
||||
"X-Device-Accept-Language",
|
||||
"X-Device-User-Agent",
|
||||
"X-Frame-Options",
|
||||
"X-Mittente",
|
||||
"X-PGP-Sig",
|
||||
"X-Ricevuta",
|
||||
"X-Riferimento-Message-ID",
|
||||
"X-TipoRicevuta",
|
||||
"X-Trasporto",
|
||||
"X-VerificaSicurezza",
|
||||
"X400-Content-Identifier",
|
||||
"X400-Content-Return",
|
||||
"X400-Content-Type",
|
||||
"X400-MTS-Identifier",
|
||||
"X400-Originator",
|
||||
"X400-Received",
|
||||
"X400-Recipients",
|
||||
"X400-Trace",
|
||||
"Xref"
|
||||
}})
|
||||
{
|
||||
for(std::size_t i = 1, n = 256; i < n; ++i)
|
||||
{
|
||||
auto sv = by_name_[ i ];
|
||||
auto h = digest(sv);
|
||||
auto j = h % N;
|
||||
BOOST_ASSERT(map_[j][0] == 0);
|
||||
map_[j][0] = static_cast<unsigned char>(i);
|
||||
}
|
||||
|
||||
for(std::size_t i = 256, n = by_name_.size(); i < n; ++i)
|
||||
{
|
||||
auto sv = by_name_[i];
|
||||
auto h = digest(sv);
|
||||
auto j = h % N;
|
||||
BOOST_ASSERT(map_[j][1] == 0);
|
||||
map_[j][1] = static_cast<unsigned char>(i - 255);
|
||||
}
|
||||
}
|
||||
|
||||
field
|
||||
string_to_field(string_view s) const
|
||||
{
|
||||
auto h = digest(s);
|
||||
auto j = h % N;
|
||||
int i = map_[j][0];
|
||||
string_view s2 = by_name_[i];
|
||||
if(i != 0 && equals(s, s2))
|
||||
return static_cast<field>(i);
|
||||
i = map_[j][1];
|
||||
if(i == 0)
|
||||
return field::unknown;
|
||||
i += 255;
|
||||
s2 = by_name_[i];
|
||||
|
||||
if(equals(s, s2))
|
||||
return static_cast<field>(i);
|
||||
return field::unknown;
|
||||
}
|
||||
|
||||
//
|
||||
// Deprecated
|
||||
//
|
||||
|
||||
using const_iterator =
|
||||
array_type::const_iterator;
|
||||
|
||||
std::size_t
|
||||
size() const
|
||||
{
|
||||
return by_name_.size();
|
||||
}
|
||||
|
||||
const_iterator
|
||||
begin() const
|
||||
{
|
||||
return by_name_.begin();
|
||||
}
|
||||
|
||||
const_iterator
|
||||
end() const
|
||||
{
|
||||
return by_name_.end();
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
field_table const&
|
||||
get_field_table()
|
||||
{
|
||||
static field_table const tab;
|
||||
return tab;
|
||||
}
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
string_view
|
||||
to_string(field f)
|
||||
{
|
||||
auto const& v = get_field_table();
|
||||
BOOST_ASSERT(static_cast<unsigned>(f) < v.size());
|
||||
return v.begin()[static_cast<unsigned>(f)];
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
string_view
|
||||
to_string(field f)
|
||||
{
|
||||
return detail::to_string(f);
|
||||
}
|
||||
|
||||
field
|
||||
string_to_field(string_view s)
|
||||
{
|
||||
return detail::get_field_table().string_to_field(s);
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, field f)
|
||||
{
|
||||
return os << to_string(f);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+1215
File diff suppressed because it is too large
Load Diff
+138
@@ -0,0 +1,138 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_FIELDS_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_FIELDS_IPP
|
||||
|
||||
#include <boost/beast/http/fields.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
// `basic_fields` assumes that `std::size_t` is larger than `uint16_t`, so we
|
||||
// verify it explicitly here, so that users that use split compilation don't
|
||||
// need to pay the (fairly small) price for this sanity check
|
||||
BOOST_STATIC_ASSERT((std::numeric_limits<std::size_t>::max)() >=
|
||||
(std::numeric_limits<std::uint32_t>::max)());
|
||||
|
||||
// Filter a token list
|
||||
//
|
||||
inline
|
||||
void
|
||||
filter_token_list(
|
||||
beast::detail::temporary_buffer& s,
|
||||
string_view value,
|
||||
iequals_predicate const& pred)
|
||||
{
|
||||
token_list te{value};
|
||||
auto it = te.begin();
|
||||
auto last = te.end();
|
||||
if(it == last)
|
||||
return;
|
||||
while(pred(*it))
|
||||
if(++it == last)
|
||||
return;
|
||||
s.append(*it);
|
||||
while(++it != last)
|
||||
{
|
||||
if(! pred(*it))
|
||||
{
|
||||
s.append(", ", *it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
filter_token_list_last(
|
||||
beast::detail::temporary_buffer& s,
|
||||
string_view value,
|
||||
iequals_predicate const& pred)
|
||||
{
|
||||
token_list te{value};
|
||||
if(te.begin() != te.end())
|
||||
{
|
||||
auto it = te.begin();
|
||||
auto next = std::next(it);
|
||||
if(next == te.end())
|
||||
{
|
||||
if(! pred(*it))
|
||||
s.append(*it);
|
||||
return;
|
||||
}
|
||||
s.append(*it);
|
||||
for(;;)
|
||||
{
|
||||
it = next;
|
||||
next = std::next(it);
|
||||
if(next == te.end())
|
||||
{
|
||||
if(! pred(*it))
|
||||
{
|
||||
s.append(", ", *it);
|
||||
}
|
||||
return;
|
||||
}
|
||||
s.append(", ", *it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
keep_alive_impl(
|
||||
beast::detail::temporary_buffer& s, string_view value,
|
||||
unsigned version, bool keep_alive)
|
||||
{
|
||||
if(version < 11)
|
||||
{
|
||||
if(keep_alive)
|
||||
{
|
||||
// remove close
|
||||
filter_token_list(s, value, iequals_predicate{"close", {}});
|
||||
// add keep-alive
|
||||
if(s.empty())
|
||||
s.append("keep-alive");
|
||||
else if(! token_list{value}.exists("keep-alive"))
|
||||
s.append(", keep-alive");
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove close and keep-alive
|
||||
filter_token_list(s, value,
|
||||
iequals_predicate{"close", "keep-alive"});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(keep_alive)
|
||||
{
|
||||
// remove close and keep-alive
|
||||
filter_token_list(s, value,
|
||||
iequals_predicate{"close", "keep-alive"});
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove keep-alive
|
||||
filter_token_list(s, value, iequals_predicate{"keep-alive", {}});
|
||||
// add close
|
||||
if(s.empty())
|
||||
s.append("close");
|
||||
else if(! token_list{value}.exists("close"))
|
||||
s.append(", close");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif // BOOST_BEAST_HTTP_IMPL_FIELDS_IPP
|
||||
+647
@@ -0,0 +1,647 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_FILE_BODY_WIN32_HPP
|
||||
|
||||
#if BOOST_BEAST_USE_WIN32_FILE
|
||||
|
||||
#include <boost/beast/core/async_base.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/buffers_range.hpp>
|
||||
#include <boost/beast/core/detail/clamp.hpp>
|
||||
#include <boost/beast/core/detail/is_invocable.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/write.hpp>
|
||||
#include <boost/beast/http/serializer.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/basic_stream_socket.hpp>
|
||||
#include <boost/asio/windows/overlapped_ptr.hpp>
|
||||
#include <boost/make_unique.hpp>
|
||||
#include <boost/smart_ptr/make_shared_array.hpp>
|
||||
#include <boost/winapi/basic_types.hpp>
|
||||
#include <boost/winapi/error_codes.hpp>
|
||||
#include <boost/winapi/get_last_error.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
template<class, class, bool, class, class>
|
||||
class write_some_win32_op;
|
||||
} // detail
|
||||
|
||||
template<>
|
||||
struct basic_file_body<file_win32>
|
||||
{
|
||||
using file_type = file_win32;
|
||||
|
||||
class writer;
|
||||
class reader;
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class value_type
|
||||
{
|
||||
friend class writer;
|
||||
friend class reader;
|
||||
friend struct basic_file_body<file_win32>;
|
||||
|
||||
template<class, class, bool, class, class>
|
||||
friend class detail::write_some_win32_op;
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
bool isRequest, class Fields>
|
||||
friend
|
||||
std::size_t
|
||||
write_some(
|
||||
net::basic_stream_socket<Protocol, Executor>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
error_code& ec);
|
||||
|
||||
file_win32 file_;
|
||||
std::uint64_t size_ = 0; // cached file size
|
||||
std::uint64_t first_; // starting offset of the range
|
||||
std::uint64_t last_; // ending offset of the range
|
||||
|
||||
public:
|
||||
~value_type() = default;
|
||||
value_type() = default;
|
||||
value_type(value_type&& other) = default;
|
||||
value_type& operator=(value_type&& other) = default;
|
||||
|
||||
file_win32& file()
|
||||
{
|
||||
return file_;
|
||||
}
|
||||
|
||||
bool
|
||||
is_open() const
|
||||
{
|
||||
return file_.is_open();
|
||||
}
|
||||
|
||||
std::uint64_t
|
||||
size() const
|
||||
{
|
||||
return last_ - first_;
|
||||
}
|
||||
|
||||
void
|
||||
close();
|
||||
|
||||
void
|
||||
open(char const* path, file_mode mode, error_code& ec);
|
||||
|
||||
void
|
||||
reset(file_win32&& file, error_code& ec);
|
||||
|
||||
void
|
||||
seek(std::uint64_t offset, error_code& ec);
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class writer
|
||||
{
|
||||
template<class, class, bool, class, class>
|
||||
friend class detail::write_some_win32_op;
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
bool isRequest, class Fields>
|
||||
friend
|
||||
std::size_t
|
||||
write_some(
|
||||
net::basic_stream_socket<Protocol, Executor>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
error_code& ec);
|
||||
|
||||
value_type& body_; // The body we are reading from
|
||||
std::uint64_t pos_; // The current position in the file
|
||||
char buf_[BOOST_BEAST_FILE_BUFFER_SIZE]; // Small buffer for reading
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
net::const_buffer;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
writer(header<isRequest, Fields>&, value_type& b)
|
||||
: body_(b)
|
||||
, pos_(body_.first_)
|
||||
{
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
ec.clear();
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
std::size_t const n = (std::min)(sizeof(buf_),
|
||||
beast::detail::clamp(body_.last_ - pos_));
|
||||
if(n == 0)
|
||||
{
|
||||
ec = {};
|
||||
return boost::none;
|
||||
}
|
||||
auto const nread = body_.file_.read(buf_, n, ec);
|
||||
if(ec)
|
||||
return boost::none;
|
||||
if (nread == 0)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::short_read);
|
||||
return boost::none;
|
||||
}
|
||||
BOOST_ASSERT(nread != 0);
|
||||
pos_ += nread;
|
||||
ec = {};
|
||||
return {{
|
||||
{buf_, nread}, // buffer to return.
|
||||
pos_ < body_.last_}}; // `true` if there are more buffers.
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class reader
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(header<isRequest, Fields>&, value_type& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> const& content_length,
|
||||
error_code& ec)
|
||||
{
|
||||
// VFALCO We could reserve space in the file
|
||||
boost::ignore_unused(content_length);
|
||||
BOOST_ASSERT(body_.file_.is_open());
|
||||
ec = {};
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
std::size_t nwritten = 0;
|
||||
for(auto buffer : beast::buffers_range_ref(buffers))
|
||||
{
|
||||
nwritten += body_.file_.write(
|
||||
buffer.data(), buffer.size(), ec);
|
||||
if(ec)
|
||||
return nwritten;
|
||||
}
|
||||
ec = {};
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& body)
|
||||
{
|
||||
return body.size();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
inline
|
||||
void
|
||||
basic_file_body<file_win32>::
|
||||
value_type::
|
||||
close()
|
||||
{
|
||||
error_code ignored;
|
||||
file_.close(ignored);
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
basic_file_body<file_win32>::
|
||||
value_type::
|
||||
open(char const* path, file_mode mode, error_code& ec)
|
||||
{
|
||||
file_.open(path, mode, ec);
|
||||
if(ec)
|
||||
return;
|
||||
size_ = file_.size(ec);
|
||||
if(ec)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
first_ = 0;
|
||||
last_ = size_;
|
||||
}
|
||||
|
||||
inline
|
||||
void
|
||||
basic_file_body<file_win32>::
|
||||
value_type::
|
||||
reset(file_win32&& file, error_code& ec)
|
||||
{
|
||||
if(file_.is_open())
|
||||
{
|
||||
error_code ignored;
|
||||
file_.close(ignored);
|
||||
}
|
||||
file_ = std::move(file);
|
||||
if(file_.is_open())
|
||||
{
|
||||
size_ = file_.size(ec);
|
||||
if(ec)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
first_ = file_.pos(ec);
|
||||
if(ec)
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
last_ = size_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
void
|
||||
basic_file_body<file_win32>::
|
||||
value_type::
|
||||
seek(std::uint64_t offset, error_code& ec)
|
||||
{
|
||||
first_ = offset;
|
||||
file_.seek(offset, ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Unsigned>
|
||||
boost::winapi::DWORD_
|
||||
lowPart(Unsigned n)
|
||||
{
|
||||
return static_cast<
|
||||
boost::winapi::DWORD_>(
|
||||
n & 0xffffffff);
|
||||
}
|
||||
|
||||
template<class Unsigned>
|
||||
boost::winapi::DWORD_
|
||||
highPart(Unsigned n, std::true_type)
|
||||
{
|
||||
return static_cast<
|
||||
boost::winapi::DWORD_>(
|
||||
(n>>32)&0xffffffff);
|
||||
}
|
||||
|
||||
template<class Unsigned>
|
||||
boost::winapi::DWORD_
|
||||
highPart(Unsigned, std::false_type)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class Unsigned>
|
||||
boost::winapi::DWORD_
|
||||
highPart(Unsigned n)
|
||||
{
|
||||
return highPart(n, std::integral_constant<
|
||||
bool, (sizeof(Unsigned)>4)>{});
|
||||
}
|
||||
|
||||
class null_lambda
|
||||
{
|
||||
public:
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code&,
|
||||
ConstBufferSequence const&) const
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
}
|
||||
};
|
||||
|
||||
// https://github.com/boostorg/beast/issues/1815
|
||||
// developer commentary:
|
||||
// This function mimics the behaviour of ASIO.
|
||||
// Perhaps the correct fix is to insist on the use
|
||||
// of an appropriate error_condition to detect
|
||||
// connection_reset and connection_refused?
|
||||
inline
|
||||
error_code
|
||||
make_win32_error(
|
||||
boost::winapi::DWORD_ dwError) noexcept
|
||||
{
|
||||
// from
|
||||
// https://github.com/boostorg/asio/blob/6534af41b471288091ae39f9ab801594189b6fc9/include/boost/asio/detail/impl/socket_ops.ipp#L842
|
||||
switch(dwError)
|
||||
{
|
||||
case boost::winapi::ERROR_NETNAME_DELETED_:
|
||||
return net::error::connection_reset;
|
||||
case boost::winapi::ERROR_PORT_UNREACHABLE_:
|
||||
return net::error::connection_refused;
|
||||
case boost::winapi::WSAEMSGSIZE_:
|
||||
case boost::winapi::ERROR_MORE_DATA_:
|
||||
return {};
|
||||
}
|
||||
return error_code(
|
||||
static_cast<int>(dwError),
|
||||
system_category());
|
||||
}
|
||||
|
||||
inline
|
||||
error_code
|
||||
make_win32_error(
|
||||
error_code ec) noexcept
|
||||
{
|
||||
if(ec.category() !=
|
||||
system_category())
|
||||
return ec;
|
||||
return make_win32_error(
|
||||
static_cast<boost::winapi::DWORD_>(
|
||||
ec.value()));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
|
||||
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
bool isRequest, class Fields,
|
||||
class Handler>
|
||||
class write_some_win32_op
|
||||
: public beast::async_base<Handler, Executor>
|
||||
{
|
||||
net::basic_stream_socket<
|
||||
Protocol, Executor>& sock_;
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr_;
|
||||
bool header_ = false;
|
||||
|
||||
public:
|
||||
template<class Handler_>
|
||||
write_some_win32_op(
|
||||
Handler_&& h,
|
||||
net::basic_stream_socket<
|
||||
Protocol, Executor>& s,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>,Fields>& sr)
|
||||
: async_base<
|
||||
Handler, Executor>(
|
||||
std::forward<Handler_>(h),
|
||||
s.get_executor())
|
||||
, sock_(s)
|
||||
, sr_(sr)
|
||||
{
|
||||
(*this)();
|
||||
}
|
||||
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
if(! sr_.is_header_done())
|
||||
{
|
||||
header_ = true;
|
||||
sr_.split(true);
|
||||
return detail::async_write_some_impl(
|
||||
sock_, sr_, std::move(*this));
|
||||
}
|
||||
if(sr_.get().chunked())
|
||||
{
|
||||
return detail::async_write_some_impl(
|
||||
sock_, sr_, std::move(*this));
|
||||
}
|
||||
auto& w = sr_.writer_impl();
|
||||
boost::winapi::DWORD_ const nNumberOfBytesToWrite =
|
||||
static_cast<boost::winapi::DWORD_>(
|
||||
(std::min<std::uint64_t>)(
|
||||
(std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr_.limit()),
|
||||
(std::numeric_limits<boost::winapi::INT_>::max)() - 1));
|
||||
net::windows::overlapped_ptr overlapped{
|
||||
sock_.get_executor(), std::move(*this)};
|
||||
// Note that we have moved *this, so we cannot access
|
||||
// the handler since it is now moved-from. We can still
|
||||
// access simple things like references and built-in types.
|
||||
auto& ov = *overlapped.get();
|
||||
ov.Offset = lowPart(w.pos_);
|
||||
ov.OffsetHigh = highPart(w.pos_);
|
||||
auto const bSuccess = ::TransmitFile(
|
||||
sock_.native_handle(),
|
||||
sr_.get().body().file_.native_handle(),
|
||||
nNumberOfBytesToWrite,
|
||||
0,
|
||||
overlapped.get(),
|
||||
nullptr,
|
||||
0);
|
||||
auto const dwError = boost::winapi::GetLastError();
|
||||
if(! bSuccess && dwError !=
|
||||
boost::winapi::ERROR_IO_PENDING_)
|
||||
{
|
||||
// VFALCO This needs review, is 0 the right number?
|
||||
// completed immediately (with error?)
|
||||
overlapped.complete(
|
||||
make_win32_error(dwError), 0);
|
||||
return;
|
||||
}
|
||||
overlapped.release();
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec,
|
||||
std::size_t bytes_transferred = 0)
|
||||
{
|
||||
if(ec)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, make_win32_error(ec));
|
||||
}
|
||||
else if(! ec && ! header_)
|
||||
{
|
||||
auto& w = sr_.writer_impl();
|
||||
w.pos_ += bytes_transferred;
|
||||
BOOST_ASSERT(w.pos_ <= w.body_.last_);
|
||||
if(w.pos_ >= w.body_.last_)
|
||||
{
|
||||
sr_.next(ec, null_lambda{});
|
||||
BOOST_ASSERT(! ec);
|
||||
BOOST_ASSERT(sr_.is_done());
|
||||
}
|
||||
}
|
||||
this->complete_now(ec, bytes_transferred);
|
||||
}
|
||||
};
|
||||
|
||||
struct run_write_some_win32_op
|
||||
{
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
bool isRequest, class Fields,
|
||||
class WriteHandler>
|
||||
void
|
||||
operator()(
|
||||
WriteHandler&& h,
|
||||
net::basic_stream_socket<
|
||||
Protocol, Executor>* s,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>* sr)
|
||||
{
|
||||
// If you get an error on the following line it means
|
||||
// that your handler does not meet the documented type
|
||||
// requirements for the handler.
|
||||
|
||||
static_assert(
|
||||
beast::detail::is_invocable<WriteHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"WriteHandler type requirements not met");
|
||||
|
||||
write_some_win32_op<
|
||||
Protocol, Executor,
|
||||
isRequest, Fields,
|
||||
typename std::decay<WriteHandler>::type>(
|
||||
std::forward<WriteHandler>(h), *s, *sr);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
bool isRequest, class Fields>
|
||||
std::size_t
|
||||
write_some(
|
||||
net::basic_stream_socket<
|
||||
Protocol, Executor>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
if(! sr.is_header_done())
|
||||
{
|
||||
sr.split(true);
|
||||
auto const bytes_transferred =
|
||||
detail::write_some_impl(sock, sr, ec);
|
||||
if(ec)
|
||||
return bytes_transferred;
|
||||
return bytes_transferred;
|
||||
}
|
||||
if(sr.get().chunked())
|
||||
{
|
||||
auto const bytes_transferred =
|
||||
detail::write_some_impl(sock, sr, ec);
|
||||
if(ec)
|
||||
return bytes_transferred;
|
||||
return bytes_transferred;
|
||||
}
|
||||
auto& w = sr.writer_impl();
|
||||
w.body_.file_.seek(w.pos_, ec);
|
||||
if(ec)
|
||||
return 0;
|
||||
boost::winapi::DWORD_ const nNumberOfBytesToWrite =
|
||||
static_cast<boost::winapi::DWORD_>(
|
||||
(std::min<std::uint64_t>)(
|
||||
(std::min<std::uint64_t>)(w.body_.last_ - w.pos_, sr.limit()),
|
||||
(std::numeric_limits<boost::winapi::INT_>::max)() - 1));
|
||||
auto const bSuccess = ::TransmitFile(
|
||||
sock.native_handle(),
|
||||
w.body_.file_.native_handle(),
|
||||
nNumberOfBytesToWrite,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0);
|
||||
if(! bSuccess)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, detail::make_win32_error(
|
||||
boost::winapi::GetLastError()));
|
||||
return 0;
|
||||
}
|
||||
w.pos_ += nNumberOfBytesToWrite;
|
||||
BOOST_ASSERT(w.pos_ <= w.body_.last_);
|
||||
if(w.pos_ < w.body_.last_)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
else
|
||||
{
|
||||
sr.next(ec, detail::null_lambda{});
|
||||
BOOST_ASSERT(! ec);
|
||||
BOOST_ASSERT(sr.is_done());
|
||||
}
|
||||
return nNumberOfBytesToWrite;
|
||||
}
|
||||
|
||||
#if BOOST_ASIO_HAS_WINDOWS_OVERLAPPED_PTR
|
||||
|
||||
template<
|
||||
class Protocol, class Executor,
|
||||
bool isRequest, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write_some(
|
||||
net::basic_stream_socket<
|
||||
Protocol, Executor>& sock,
|
||||
serializer<isRequest,
|
||||
basic_file_body<file_win32>, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
return net::async_initiate<
|
||||
WriteHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::run_write_some_win32_op{},
|
||||
handler,
|
||||
&sock,
|
||||
&sr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+428
@@ -0,0 +1,428 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_MESSAGE_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_MESSAGE_HPP
|
||||
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<class Fields>
|
||||
template<class Arg1, class... ArgN, class>
|
||||
header<true, Fields>::
|
||||
header(Arg1&& arg1, ArgN&&... argn)
|
||||
: Fields(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
verb
|
||||
header<true, Fields>::
|
||||
method() const
|
||||
{
|
||||
return method_;
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
header<true, Fields>::
|
||||
method(verb v)
|
||||
{
|
||||
if(v == verb::unknown)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument{"unknown method"});
|
||||
method_ = v;
|
||||
this->set_method_impl({});
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
string_view
|
||||
header<true, Fields>::
|
||||
method_string() const
|
||||
{
|
||||
if(method_ != verb::unknown)
|
||||
return to_string(method_);
|
||||
return this->get_method_impl();
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
header<true, Fields>::
|
||||
method_string(string_view s)
|
||||
{
|
||||
method_ = string_to_verb(s);
|
||||
if(method_ != verb::unknown)
|
||||
this->set_method_impl({});
|
||||
else
|
||||
this->set_method_impl(s);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
string_view
|
||||
header<true, Fields>::
|
||||
target() const
|
||||
{
|
||||
return this->get_target_impl();
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
header<true, Fields>::
|
||||
target(string_view s)
|
||||
{
|
||||
this->set_target_impl(s);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
swap(
|
||||
header<true, Fields>& h1,
|
||||
header<true, Fields>& h2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(
|
||||
static_cast<Fields&>(h1),
|
||||
static_cast<Fields&>(h2));
|
||||
swap(h1.version_, h2.version_);
|
||||
swap(h1.method_, h2.method_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Fields>
|
||||
template<class Arg1, class... ArgN, class>
|
||||
header<false, Fields>::
|
||||
header(Arg1&& arg1, ArgN&&... argn)
|
||||
: Fields(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
status
|
||||
header<false, Fields>::
|
||||
result() const
|
||||
{
|
||||
return int_to_status(
|
||||
static_cast<int>(result_));
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
header<false, Fields>::
|
||||
result(status v)
|
||||
{
|
||||
result_ = v;
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
header<false, Fields>::
|
||||
result(unsigned v)
|
||||
{
|
||||
if(v > 999)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
std::invalid_argument{
|
||||
"invalid status-code"});
|
||||
result_ = static_cast<status>(v);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
unsigned
|
||||
header<false, Fields>::
|
||||
result_int() const
|
||||
{
|
||||
return static_cast<unsigned>(result_);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
string_view
|
||||
header<false, Fields>::
|
||||
reason() const
|
||||
{
|
||||
auto const s = this->get_reason_impl();
|
||||
if(! s.empty())
|
||||
return s;
|
||||
return obsolete_reason(result_);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
header<false, Fields>::
|
||||
reason(string_view s)
|
||||
{
|
||||
this->set_reason_impl(s);
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
void
|
||||
swap(
|
||||
header<false, Fields>& h1,
|
||||
header<false, Fields>& h2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(
|
||||
static_cast<Fields&>(h1),
|
||||
static_cast<Fields&>(h2));
|
||||
swap(h1.version_, h2.version_);
|
||||
swap(h1.result_, h2.result_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... BodyArgs>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(header_type&& h, BodyArgs&&... body_args)
|
||||
: header_type(std::move(h))
|
||||
, boost::empty_value<
|
||||
typename Body::value_type>(boost::empty_init_t(),
|
||||
std::forward<BodyArgs>(body_args)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... BodyArgs>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(header_type const& h, BodyArgs&&... body_args)
|
||||
: header_type(h)
|
||||
, boost::empty_value<
|
||||
typename Body::value_type>(boost::empty_init_t(),
|
||||
std::forward<BodyArgs>(body_args)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(verb method, string_view target, Version version)
|
||||
: header_type(method, target, version)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class BodyArg, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(verb method, string_view target,
|
||||
Version version, BodyArg&& body_arg)
|
||||
: header_type(method, target, version)
|
||||
, boost::empty_value<
|
||||
typename Body::value_type>(boost::empty_init_t(),
|
||||
std::forward<BodyArg>(body_arg))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class BodyArg, class FieldsArg, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(
|
||||
verb method, string_view target, Version version,
|
||||
BodyArg&& body_arg,
|
||||
FieldsArg&& fields_arg)
|
||||
: header_type(method, target, version,
|
||||
std::forward<FieldsArg>(fields_arg))
|
||||
, boost::empty_value<
|
||||
typename Body::value_type>(boost::empty_init_t(),
|
||||
std::forward<BodyArg>(body_arg))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(status result, Version version)
|
||||
: header_type(result, version)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class BodyArg, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(status result, Version version,
|
||||
BodyArg&& body_arg)
|
||||
: header_type(result, version)
|
||||
, boost::empty_value<
|
||||
typename Body::value_type>(boost::empty_init_t(),
|
||||
std::forward<BodyArg>(body_arg))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Version, class BodyArg, class FieldsArg, class>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(status result, Version version,
|
||||
BodyArg&& body_arg, FieldsArg&& fields_arg)
|
||||
: header_type(result, version,
|
||||
std::forward<FieldsArg>(fields_arg))
|
||||
, boost::empty_value<
|
||||
typename Body::value_type>(boost::empty_init_t(),
|
||||
std::forward<BodyArg>(body_arg))
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(std::piecewise_construct_t)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... BodyArgs>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(std::piecewise_construct_t,
|
||||
std::tuple<BodyArgs...> body_args)
|
||||
: message(std::piecewise_construct,
|
||||
body_args,
|
||||
mp11::make_index_sequence<
|
||||
sizeof...(BodyArgs)>{})
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... BodyArgs, class... FieldsArgs>
|
||||
message<isRequest, Body, Fields>::
|
||||
message(std::piecewise_construct_t,
|
||||
std::tuple<BodyArgs...> body_args,
|
||||
std::tuple<FieldsArgs...> fields_args)
|
||||
: message(std::piecewise_construct,
|
||||
body_args,
|
||||
fields_args,
|
||||
mp11::make_index_sequence<
|
||||
sizeof...(BodyArgs)>{},
|
||||
mp11::make_index_sequence<
|
||||
sizeof...(FieldsArgs)>{})
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
chunked(bool value)
|
||||
{
|
||||
this->set_chunked_impl(value);
|
||||
this->set_content_length_impl(boost::none);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
content_length(
|
||||
boost::optional<std::uint64_t> const& value)
|
||||
{
|
||||
this->set_content_length_impl(value);
|
||||
this->set_chunked_impl(false);
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
boost::optional<std::uint64_t>
|
||||
message<isRequest, Body, Fields>::
|
||||
payload_size() const
|
||||
{
|
||||
return payload_size(detail::is_body_sized<Body>{});
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
bool
|
||||
message<isRequest, Body, Fields>::
|
||||
need_eof(std::false_type) const
|
||||
{
|
||||
// VFALCO Do we need a way to let the caller say "the body is intentionally skipped"?
|
||||
if( this->result() == status::no_content ||
|
||||
this->result() == status::not_modified ||
|
||||
to_status_class(this->result()) ==
|
||||
status_class::informational ||
|
||||
has_content_length() ||
|
||||
chunked())
|
||||
return ! keep_alive();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare_payload(std::true_type)
|
||||
{
|
||||
auto const n = payload_size();
|
||||
if(this->method() == verb::trace && (! n || *n > 0))
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid request body"});
|
||||
if(n)
|
||||
{
|
||||
if(*n > 0 ||
|
||||
this->method() == verb::options ||
|
||||
this->method() == verb::put ||
|
||||
this->method() == verb::post)
|
||||
{
|
||||
this->content_length(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->chunked(false);
|
||||
}
|
||||
}
|
||||
else if(this->version() == 11)
|
||||
{
|
||||
this->chunked(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->chunked(false);
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
message<isRequest, Body, Fields>::
|
||||
prepare_payload(std::false_type)
|
||||
{
|
||||
auto const n = payload_size();
|
||||
if( (! n || *n > 0) && (
|
||||
(status_class(this->result()) == status_class::informational ||
|
||||
this->result() == status::no_content ||
|
||||
this->result() == status::not_modified)))
|
||||
{
|
||||
// The response body MUST be empty for this case
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"invalid response body"});
|
||||
}
|
||||
if(n)
|
||||
this->content_length(n);
|
||||
else if(this->version() == 11)
|
||||
this->chunked(true);
|
||||
else
|
||||
this->chunked(false);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void
|
||||
swap(
|
||||
message<isRequest, Body, Fields>& m1,
|
||||
message<isRequest, Body, Fields>& m2)
|
||||
{
|
||||
using std::swap;
|
||||
swap(
|
||||
static_cast<header<isRequest, Fields>&>(m1),
|
||||
static_cast<header<isRequest, Fields>&>(m2));
|
||||
swap(m1.body(), m2.body());
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Copyright (c) 2022 Seth Heeren (sgheeren 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_MESSAGE_GENERATOR_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_MESSAGE_GENERATOR_HPP
|
||||
|
||||
#include <boost/beast/http/message_generator.hpp>
|
||||
#include <boost/smart_ptr/make_unique.hpp>
|
||||
#include <boost/beast/core/buffers_generator.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template <bool isRequest, class Body, class Fields>
|
||||
message_generator::message_generator(
|
||||
http::message<isRequest, Body, Fields>&& m)
|
||||
: impl_(boost::make_unique<
|
||||
generator_impl<isRequest, Body, Fields>>(
|
||||
std::move(m)))
|
||||
{
|
||||
}
|
||||
|
||||
template <bool isRequest, class Body, class Fields>
|
||||
struct message_generator::generator_impl
|
||||
: message_generator::impl_base
|
||||
{
|
||||
explicit generator_impl(
|
||||
http::message<isRequest, Body, Fields>&& m)
|
||||
: m_(std::move(m))
|
||||
, sr_(m_)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
is_done() override
|
||||
{
|
||||
return sr_.is_done();
|
||||
}
|
||||
|
||||
const_buffers_type
|
||||
prepare(error_code& ec) override
|
||||
{
|
||||
sr_.next(ec, visit{*this});
|
||||
return current_;
|
||||
}
|
||||
|
||||
void
|
||||
consume(std::size_t n) override
|
||||
{
|
||||
sr_.consume((std::min)(n, beast::buffer_bytes(current_)));
|
||||
}
|
||||
|
||||
bool
|
||||
keep_alive() const noexcept override
|
||||
{
|
||||
return m_.keep_alive();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr unsigned max_fixed_bufs = 12;
|
||||
|
||||
http::message<isRequest, Body, Fields> m_;
|
||||
http::serializer<isRequest, Body, Fields> sr_;
|
||||
|
||||
std::array<net::const_buffer, max_fixed_bufs> bs_;
|
||||
const_buffers_type current_ = bs_; // subspan
|
||||
|
||||
struct visit
|
||||
{
|
||||
generator_impl& self_;
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code&, ConstBufferSequence const& buffers)
|
||||
{
|
||||
auto& s = self_.bs_;
|
||||
auto& cur = self_.current_;
|
||||
|
||||
auto it = net::buffer_sequence_begin(buffers);
|
||||
|
||||
std::size_t n =
|
||||
std::distance(it, net::buffer_sequence_end(buffers));
|
||||
|
||||
n = (std::min)(s.size(), n);
|
||||
|
||||
cur = { s.data(), n };
|
||||
std::copy_n(it, n, cur.begin());
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace beast
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_PARSER_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_PARSER_HPP
|
||||
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Body, class Allocator>
|
||||
parser<isRequest, Body, Allocator>::
|
||||
parser()
|
||||
: rd_(m_.base(), m_.body())
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Allocator>
|
||||
template<class Arg1, class... ArgN, class>
|
||||
parser<isRequest, Body, Allocator>::
|
||||
parser(Arg1&& arg1, ArgN&&... argn)
|
||||
: m_(
|
||||
std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
, rd_(m_.base(), m_.body())
|
||||
{
|
||||
m_.clear();
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Allocator>
|
||||
template<class OtherBody, class... Args, class>
|
||||
parser<isRequest, Body, Allocator>::
|
||||
parser(
|
||||
parser<isRequest, OtherBody, Allocator>&& other,
|
||||
Args&&... args)
|
||||
: basic_parser<isRequest>(std::move(other))
|
||||
, m_(other.release(), std::forward<Args>(args)...)
|
||||
, rd_(m_.base(), m_.body())
|
||||
{
|
||||
if(other.rd_inited_)
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{
|
||||
"moved-from parser has a body"});
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+722
@@ -0,0 +1,722 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
// Copyright (c) 2020 Richard Hodges (hodges.r@gmail.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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_READ_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_READ_HPP
|
||||
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/parser.hpp>
|
||||
#include <boost/beast/http/read.hpp>
|
||||
#include <boost/beast/core/async_base.hpp>
|
||||
#include <boost/beast/core/stream_traits.hpp>
|
||||
#include <boost/beast/core/detail/buffer.hpp>
|
||||
#include <boost/beast/core/detail/read.hpp>
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct parser_is_done
|
||||
{
|
||||
template<bool isRequest>
|
||||
bool
|
||||
operator()(basic_parser<isRequest> const& p) const
|
||||
{
|
||||
return p.is_done();
|
||||
}
|
||||
};
|
||||
|
||||
struct parser_is_header_done
|
||||
{
|
||||
template<bool isRequest>
|
||||
bool
|
||||
operator()(basic_parser<isRequest> const& p) const
|
||||
{
|
||||
return p.is_header_done();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator,
|
||||
class Handler>
|
||||
class read_msg_op
|
||||
: public beast::stable_async_base<
|
||||
Handler, beast::executor_type<Stream>>
|
||||
, public asio::coroutine
|
||||
{
|
||||
using parser_type =
|
||||
parser<isRequest, Body, Allocator>;
|
||||
|
||||
using message_type =
|
||||
typename parser_type::value_type;
|
||||
|
||||
struct data
|
||||
{
|
||||
Stream& s;
|
||||
message_type& m;
|
||||
parser_type p;
|
||||
|
||||
data(
|
||||
Stream& s_,
|
||||
message_type& m_)
|
||||
: s(s_)
|
||||
, m(m_)
|
||||
, p(std::move(m))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
data& d_;
|
||||
|
||||
public:
|
||||
template<class Handler_>
|
||||
read_msg_op(
|
||||
Handler_&& h,
|
||||
Stream& s,
|
||||
DynamicBuffer& b,
|
||||
message_type& m)
|
||||
: stable_async_base<
|
||||
Handler, beast::executor_type<Stream>>(
|
||||
std::forward<Handler_>(h), s.get_executor())
|
||||
, d_(beast::allocate_stable<data>(
|
||||
*this, s, m))
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_read(msg)"));
|
||||
|
||||
http::async_read(d_.s, b, d_.p, std::move(*this));
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
if(! ec)
|
||||
d_.m = d_.p.release();
|
||||
this->complete_now(ec, bytes_transferred);
|
||||
}
|
||||
};
|
||||
|
||||
struct run_read_msg_op
|
||||
{
|
||||
template<
|
||||
class ReadHandler,
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
void
|
||||
operator()(
|
||||
ReadHandler&& h,
|
||||
AsyncReadStream* s,
|
||||
DynamicBuffer* b,
|
||||
message<isRequest, Body,
|
||||
basic_fields<Allocator>>* m)
|
||||
{
|
||||
// If you get an error on the following line it means
|
||||
// that your handler does not meet the documented type
|
||||
// requirements for the handler.
|
||||
|
||||
static_assert(
|
||||
beast::detail::is_invocable<ReadHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"ReadHandler type requirements not met");
|
||||
|
||||
read_msg_op<
|
||||
AsyncReadStream,
|
||||
DynamicBuffer,
|
||||
isRequest, Body, Allocator,
|
||||
typename std::decay<ReadHandler>::type>(
|
||||
std::forward<ReadHandler>(h), *s, *b, *m);
|
||||
}
|
||||
};
|
||||
|
||||
template<class AsyncReadStream, class DynamicBuffer, bool isRequest>
|
||||
class read_some_op : asio::coroutine
|
||||
{
|
||||
AsyncReadStream& s_;
|
||||
DynamicBuffer& b_;
|
||||
basic_parser<isRequest>& p_;
|
||||
std::size_t bytes_transferred_;
|
||||
bool cont_;
|
||||
|
||||
public:
|
||||
read_some_op(
|
||||
AsyncReadStream& s,
|
||||
DynamicBuffer& b,
|
||||
basic_parser<isRequest>& p)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, p_(p)
|
||||
, bytes_transferred_(0)
|
||||
, cont_(false)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Self>
|
||||
void operator()(
|
||||
Self& self,
|
||||
error_code ec = {},
|
||||
std::size_t bytes_transferred = 0)
|
||||
{
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
if(b_.size() == 0)
|
||||
goto do_read;
|
||||
for(;;)
|
||||
{
|
||||
// parse
|
||||
{
|
||||
auto const used = p_.put(b_.data(), ec);
|
||||
bytes_transferred_ += used;
|
||||
b_.consume(used);
|
||||
}
|
||||
if(ec != http::error::need_more)
|
||||
break;
|
||||
|
||||
do_read:
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
cont_ = true;
|
||||
// VFALCO This was read_size_or_throw
|
||||
auto const size = read_size(b_, 65536);
|
||||
if(size == 0)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::buffer_overflow);
|
||||
goto upcall;
|
||||
}
|
||||
auto const mb =
|
||||
beast::detail::dynamic_buffer_prepare(
|
||||
b_, size, ec, error::buffer_overflow);
|
||||
if(ec)
|
||||
goto upcall;
|
||||
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_read_some"));
|
||||
|
||||
s_.async_read_some(*mb, std::move(self));
|
||||
}
|
||||
b_.commit(bytes_transferred);
|
||||
if(ec == net::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
if(p_.got_some())
|
||||
{
|
||||
// caller sees EOF on next read
|
||||
ec.assign(0, ec.category());
|
||||
p_.put_eof(ec);
|
||||
if(ec)
|
||||
goto upcall;
|
||||
BOOST_ASSERT(p_.is_done());
|
||||
goto upcall;
|
||||
}
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::end_of_stream);
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
break;
|
||||
}
|
||||
|
||||
upcall:
|
||||
if(! cont_)
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_read_some"));
|
||||
|
||||
|
||||
const auto ex =
|
||||
asio::get_associated_immediate_executor(
|
||||
self, s_.get_executor());
|
||||
|
||||
net::dispatch(
|
||||
ex,
|
||||
beast::bind_front_handler(std::move(self), ec));
|
||||
}
|
||||
}
|
||||
self.complete(ec, bytes_transferred_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer, bool isRequest, class Condition>
|
||||
class read_op
|
||||
: asio::coroutine
|
||||
{
|
||||
Stream& s_;
|
||||
DynamicBuffer& b_;
|
||||
basic_parser<isRequest>& p_;
|
||||
std::size_t bytes_transferred_;
|
||||
|
||||
public:
|
||||
read_op(Stream& s, DynamicBuffer& b, basic_parser<isRequest>& p)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, p_(p)
|
||||
, bytes_transferred_(0)
|
||||
{
|
||||
}
|
||||
|
||||
template<class Self>
|
||||
void operator()(Self& self, error_code ec = {}, std::size_t bytes_transferred = 0)
|
||||
{
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
if (Condition{}(p_))
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_read"));
|
||||
|
||||
const auto ex =
|
||||
asio::get_associated_immediate_executor(
|
||||
self, s_.get_executor());
|
||||
|
||||
net::dispatch(ex, std::move(self));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_read"));
|
||||
|
||||
async_read_some(
|
||||
s_, b_, p_, std::move(self));
|
||||
}
|
||||
bytes_transferred_ += bytes_transferred;
|
||||
} while (!ec &&
|
||||
!Condition{}(p_));
|
||||
}
|
||||
self.complete(ec, bytes_transferred_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_some(SyncReadStream& s, DynamicBuffer& b, basic_parser<isRequest>& p, error_code& ec)
|
||||
{
|
||||
std::size_t total = 0;
|
||||
ec.clear();
|
||||
if(b.size() == 0)
|
||||
goto do_read;
|
||||
for(;;)
|
||||
{
|
||||
// parse
|
||||
{
|
||||
auto const used = p.put(b.data(), ec);
|
||||
total += used;
|
||||
b.consume(used);
|
||||
}
|
||||
if(ec != http::error::need_more)
|
||||
break;
|
||||
|
||||
do_read:
|
||||
// VFALCO This was read_size_or_throw
|
||||
auto const size = read_size(b, 65536);
|
||||
if(size == 0)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::buffer_overflow);
|
||||
return total;
|
||||
}
|
||||
auto const mb =
|
||||
beast::detail::dynamic_buffer_prepare(
|
||||
b, size, ec, error::buffer_overflow);
|
||||
if(ec)
|
||||
return total;
|
||||
std::size_t
|
||||
bytes_transferred =
|
||||
s.read_some(*mb, ec);
|
||||
b.commit(bytes_transferred);
|
||||
if(ec == net::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
if(p.got_some())
|
||||
{
|
||||
// caller sees EOF on next read
|
||||
ec.assign(0, ec.category());
|
||||
p.put_eof(ec);
|
||||
if(ec)
|
||||
return total;
|
||||
BOOST_ASSERT(p.is_done());
|
||||
return total;
|
||||
}
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::end_of_stream);
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
break;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
template<class Condition, class Stream, class DynamicBuffer, bool isRequest>
|
||||
std::size_t sync_read_op(Stream& s, DynamicBuffer& b, basic_parser<isRequest>& p, error_code& ec)
|
||||
{
|
||||
std::size_t total = 0;
|
||||
ec.clear();
|
||||
|
||||
if (!Condition{}(p))
|
||||
{
|
||||
do
|
||||
{
|
||||
total +=
|
||||
detail::read_some(s, b, p, ec);
|
||||
} while (!ec &&
|
||||
!Condition{}(p));
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
http::read_some(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
return detail::read_some(stream, buffer, parser, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
return net::async_compose<ReadHandler,
|
||||
void(beast::error_code, std::size_t)>(
|
||||
detail::read_some_op<AsyncReadStream, DynamicBuffer, isRequest> {
|
||||
stream,
|
||||
buffer,
|
||||
parser
|
||||
},
|
||||
handler,
|
||||
stream);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
http::read_header(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
parser.eager(false);
|
||||
return detail::sync_read_op<
|
||||
detail::parser_is_header_done>(
|
||||
stream, buffer, parser, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
|
||||
async_read_header(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
parser.eager(false);
|
||||
return net::async_compose<
|
||||
ReadHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::read_op<
|
||||
AsyncReadStream,
|
||||
DynamicBuffer,
|
||||
isRequest,
|
||||
detail::parser_is_header_done>(
|
||||
stream, buffer, parser),
|
||||
handler, stream);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
http::read(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
parser.eager(true);
|
||||
return detail::sync_read_op<
|
||||
detail::parser_is_done>(
|
||||
stream, buffer, parser, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(
|
||||
is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
parser.eager(true);
|
||||
return net::async_compose<
|
||||
ReadHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::read_op<
|
||||
AsyncReadStream,
|
||||
DynamicBuffer,
|
||||
isRequest,
|
||||
detail::parser_is_done>(
|
||||
stream, buffer, parser),
|
||||
handler, stream);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
std::size_t
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
http::read(stream, buffer, msg, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
std::size_t
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(
|
||||
is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader type requirements not met");
|
||||
parser<isRequest, Body, Allocator> p(std::move(msg));
|
||||
p.eager(true);
|
||||
auto const bytes_transferred =
|
||||
http::read(stream, buffer, p, ec);
|
||||
if(ec)
|
||||
return bytes_transferred;
|
||||
msg = p.release();
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(
|
||||
is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream type requirements not met");
|
||||
static_assert(
|
||||
net::is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader type requirements not met");
|
||||
return net::async_initiate<
|
||||
ReadHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::run_read_msg_op{},
|
||||
handler, &stream, &buffer, &msg);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+389
@@ -0,0 +1,389 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_RFC7230_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_RFC7230_HPP
|
||||
|
||||
#include <boost/beast/http/detail/rfc7230.hpp>
|
||||
#include <iterator>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class param_list::const_iterator
|
||||
{
|
||||
using iter_type = string_view::const_iterator;
|
||||
|
||||
std::string s_;
|
||||
detail::param_iter pi_;
|
||||
|
||||
public:
|
||||
using value_type = param_list::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return
|
||||
other.pi_.it == pi_.it &&
|
||||
other.pi_.last == pi_.last &&
|
||||
other.pi_.first == pi_.first;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return pi_.v;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &*(*this);
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class param_list;
|
||||
|
||||
const_iterator(iter_type first, iter_type last)
|
||||
{
|
||||
pi_.it = first;
|
||||
pi_.first = first;
|
||||
pi_.last = last;
|
||||
increment();
|
||||
}
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
static
|
||||
void
|
||||
unquote(string_view sr, std::string & s);
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
increment();
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
param_list::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
param_list::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
param_list::
|
||||
cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
param_list::
|
||||
cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class ext_list::const_iterator
|
||||
{
|
||||
ext_list::value_type v_;
|
||||
iter_type it_;
|
||||
iter_type first_;
|
||||
iter_type last_;
|
||||
|
||||
public:
|
||||
using value_type = ext_list::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return
|
||||
other.it_ == it_ &&
|
||||
other.first_ == first_ &&
|
||||
other.last_ == last_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &*(*this);
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ext_list;
|
||||
|
||||
const_iterator(iter_type begin, iter_type end)
|
||||
{
|
||||
it_ = begin;
|
||||
first_ = begin;
|
||||
last_ = end;
|
||||
increment();
|
||||
}
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
increment();
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
ext_list::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
ext_list::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
ext_list::
|
||||
cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
ext_list::
|
||||
cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
class token_list::const_iterator
|
||||
{
|
||||
token_list::value_type v_;
|
||||
iter_type it_;
|
||||
iter_type first_;
|
||||
iter_type last_;
|
||||
|
||||
public:
|
||||
using value_type = token_list::value_type;
|
||||
using pointer = value_type const*;
|
||||
using reference = value_type const&;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
|
||||
bool
|
||||
operator==(const_iterator const& other) const
|
||||
{
|
||||
return
|
||||
other.it_ == it_ &&
|
||||
other.first_ == first_ &&
|
||||
other.last_ == last_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(const_iterator const& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return &*(*this);
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class token_list;
|
||||
|
||||
const_iterator(iter_type begin, iter_type end)
|
||||
{
|
||||
it_ = begin;
|
||||
first_ = begin;
|
||||
last_ = end;
|
||||
increment();
|
||||
}
|
||||
|
||||
BOOST_BEAST_DECL
|
||||
void
|
||||
increment();
|
||||
};
|
||||
|
||||
inline
|
||||
auto
|
||||
token_list::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
token_list::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
token_list::
|
||||
cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.begin(), s_.end()};
|
||||
}
|
||||
|
||||
inline
|
||||
auto
|
||||
token_list::
|
||||
cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{s_.end(), s_.end()};
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
bool
|
||||
validate_list(detail::basic_parsed_list<
|
||||
Policy> const& list)
|
||||
{
|
||||
auto const last = list.end();
|
||||
auto it = list.begin();
|
||||
if(it.error())
|
||||
return false;
|
||||
while(it != last)
|
||||
{
|
||||
++it;
|
||||
if(it.error())
|
||||
return false;
|
||||
if(it == last)
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#ifdef BOOST_BEAST_HEADER_ONLY
|
||||
#include <boost/beast/http/impl/rfc7230.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
|
||||
|
||||
#include <boost/beast/http/rfc7230.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
|
||||
void param_list::const_iterator::
|
||||
unquote(string_view sr, std::string &s)
|
||||
{
|
||||
s.clear();
|
||||
s.reserve(sr.size());
|
||||
auto it = sr.begin() + 1;
|
||||
auto end = sr.end() - 1;
|
||||
while(it != end)
|
||||
{
|
||||
if(*it == '\\')
|
||||
++it;
|
||||
s.push_back(*it);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
param_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
s_.clear();
|
||||
pi_.increment();
|
||||
if(pi_.empty())
|
||||
{
|
||||
pi_.it = pi_.last;
|
||||
pi_.first = pi_.last;
|
||||
}
|
||||
else if(! pi_.v.second.empty() &&
|
||||
pi_.v.second.front() == '"')
|
||||
{
|
||||
unquote(pi_.v.second, s_);
|
||||
pi_.v.second = string_view{
|
||||
s_.data(), s_.size()};
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ext_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
/*
|
||||
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
||||
ext = token param-list
|
||||
param-list = *( OWS ";" OWS param )
|
||||
param = token OWS "=" OWS ( token / quoted-string )
|
||||
|
||||
chunked;a=b;i=j;gzip;windowBits=12
|
||||
x,y
|
||||
,,,,,chameleon
|
||||
*/
|
||||
auto const err =
|
||||
[&]
|
||||
{
|
||||
it_ = last_;
|
||||
first_ = last_;
|
||||
};
|
||||
auto need_comma = it_ != first_;
|
||||
v_.first = {};
|
||||
first_ = it_;
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it_, last_);
|
||||
if(it_ == last_)
|
||||
return err();
|
||||
auto const c = *it_;
|
||||
if(detail::is_token_char(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return err();
|
||||
auto const p0 = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == last_)
|
||||
break;
|
||||
if(! detail::is_token_char(*it_))
|
||||
break;
|
||||
}
|
||||
v_.first = string_view{&*p0,
|
||||
static_cast<std::size_t>(it_ - p0)};
|
||||
if (it_ == last_)
|
||||
return;
|
||||
detail::param_iter pi;
|
||||
pi.it = it_;
|
||||
pi.first = it_;
|
||||
pi.last = last_;
|
||||
for(;;)
|
||||
{
|
||||
pi.increment();
|
||||
if(pi.empty())
|
||||
break;
|
||||
}
|
||||
v_.second = param_list{string_view{&*it_,
|
||||
static_cast<std::size_t>(pi.it - it_)}};
|
||||
it_ = pi.it;
|
||||
return;
|
||||
}
|
||||
if(c != ',')
|
||||
return err();
|
||||
need_comma = false;
|
||||
++it_;
|
||||
}
|
||||
}
|
||||
|
||||
auto
|
||||
ext_list::
|
||||
find(string_view const& s) -> const_iterator
|
||||
{
|
||||
return std::find_if(begin(), end(),
|
||||
[&s](value_type const& v)
|
||||
{
|
||||
return beast::iequals(s, v.first);
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
ext_list::
|
||||
exists(string_view const& s)
|
||||
{
|
||||
return find(s) != end();
|
||||
}
|
||||
|
||||
void
|
||||
token_list::const_iterator::
|
||||
increment()
|
||||
{
|
||||
/*
|
||||
token-list = *( "," OWS ) token *( OWS "," [ OWS ext ] )
|
||||
*/
|
||||
auto const err =
|
||||
[&]
|
||||
{
|
||||
it_ = last_;
|
||||
first_ = last_;
|
||||
};
|
||||
auto need_comma = it_ != first_;
|
||||
v_ = {};
|
||||
first_ = it_;
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it_, last_);
|
||||
if(it_ == last_)
|
||||
return err();
|
||||
auto const c = *it_;
|
||||
if(detail::is_token_char(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return err();
|
||||
auto const p0 = it_;
|
||||
for(;;)
|
||||
{
|
||||
++it_;
|
||||
if(it_ == last_)
|
||||
break;
|
||||
if(! detail::is_token_char(*it_))
|
||||
break;
|
||||
}
|
||||
v_ = string_view{&*p0,
|
||||
static_cast<std::size_t>(it_ - p0)};
|
||||
return;
|
||||
}
|
||||
if(c != ',')
|
||||
return err();
|
||||
need_comma = false;
|
||||
++it_;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
token_list::
|
||||
exists(string_view const& s)
|
||||
{
|
||||
return std::find_if(begin(), end(),
|
||||
[&s](value_type const& v)
|
||||
{
|
||||
return beast::iequals(s, v);
|
||||
}
|
||||
) != end();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif // BOOST_BEAST_HTTP_IMPL_RFC7230_IPP
|
||||
+431
@@ -0,0 +1,431 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_SERIALIZER_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_SERIALIZER_HPP
|
||||
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#include <boost/beast/core/detail/buffers_ref.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/status.hpp>
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <ostream>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
fwrinit(std::true_type)
|
||||
{
|
||||
fwr_.emplace(m_, m_.version(), m_.method());
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
fwrinit(std::false_type)
|
||||
{
|
||||
fwr_.emplace(m_, m_.version(), m_.result_int());
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
template<std::size_t I, class Visit>
|
||||
inline
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
do_visit(error_code& ec, Visit& visit)
|
||||
{
|
||||
pv_.template emplace<I>(limit_, v_.template get<I>());
|
||||
visit(ec, beast::detail::make_buffers_ref(
|
||||
pv_.template get<I>()));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
serializer<isRequest, Body, Fields>::
|
||||
serializer(value_type& m)
|
||||
: m_(m)
|
||||
, wr_(m_.base(), m_.body())
|
||||
{
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
template<class Visit>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
next(error_code& ec, Visit&& visit)
|
||||
{
|
||||
switch(s_)
|
||||
{
|
||||
case do_construct:
|
||||
{
|
||||
fwrinit(std::integral_constant<bool,
|
||||
isRequest>{});
|
||||
if(m_.chunked())
|
||||
goto go_init_c;
|
||||
s_ = do_init;
|
||||
BOOST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_init:
|
||||
{
|
||||
wr_.init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(split_)
|
||||
goto go_header_only;
|
||||
auto result = wr_.get(ec);
|
||||
if(ec == error::need_more)
|
||||
goto go_header_only;
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_header_only;
|
||||
more_ = result->second;
|
||||
v_.template emplace<2>(
|
||||
boost::in_place_init,
|
||||
fwr_->get(),
|
||||
result->first);
|
||||
s_ = do_header;
|
||||
BOOST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_header:
|
||||
do_visit<2>(ec, visit);
|
||||
break;
|
||||
|
||||
go_header_only:
|
||||
v_.template emplace<1>(fwr_->get());
|
||||
s_ = do_header_only;
|
||||
BOOST_FALLTHROUGH;
|
||||
case do_header_only:
|
||||
do_visit<1>(ec, visit);
|
||||
break;
|
||||
|
||||
case do_body:
|
||||
s_ = do_body + 1;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
case do_body + 1:
|
||||
{
|
||||
auto result = wr_.get(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_complete;
|
||||
more_ = result->second;
|
||||
v_.template emplace<3>(result->first);
|
||||
s_ = do_body + 2;
|
||||
BOOST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_body + 2:
|
||||
do_visit<3>(ec, visit);
|
||||
break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
go_init_c:
|
||||
s_ = do_init_c;
|
||||
BOOST_FALLTHROUGH;
|
||||
case do_init_c:
|
||||
{
|
||||
wr_.init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(split_)
|
||||
goto go_header_only_c;
|
||||
auto result = wr_.get(ec);
|
||||
if(ec == error::need_more)
|
||||
goto go_header_only_c;
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_header_only_c;
|
||||
more_ = result->second;
|
||||
if(! more_)
|
||||
{
|
||||
// do it all in one buffer
|
||||
v_.template emplace<7>(
|
||||
boost::in_place_init,
|
||||
fwr_->get(),
|
||||
buffer_bytes(result->first),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{},
|
||||
detail::chunk_last(),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{});
|
||||
goto go_all_c;
|
||||
}
|
||||
v_.template emplace<4>(
|
||||
boost::in_place_init,
|
||||
fwr_->get(),
|
||||
buffer_bytes(result->first),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{});
|
||||
s_ = do_header_c;
|
||||
BOOST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_header_c:
|
||||
do_visit<4>(ec, visit);
|
||||
break;
|
||||
|
||||
go_header_only_c:
|
||||
v_.template emplace<1>(fwr_->get());
|
||||
s_ = do_header_only_c;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
case do_header_only_c:
|
||||
do_visit<1>(ec, visit);
|
||||
break;
|
||||
|
||||
case do_body_c:
|
||||
s_ = do_body_c + 1;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
case do_body_c + 1:
|
||||
{
|
||||
auto result = wr_.get(ec);
|
||||
if(ec)
|
||||
return;
|
||||
if(! result)
|
||||
goto go_final_c;
|
||||
more_ = result->second;
|
||||
if(! more_)
|
||||
{
|
||||
// do it all in one buffer
|
||||
v_.template emplace<6>(
|
||||
boost::in_place_init,
|
||||
buffer_bytes(result->first),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{},
|
||||
detail::chunk_last(),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{});
|
||||
goto go_body_final_c;
|
||||
}
|
||||
v_.template emplace<5>(
|
||||
boost::in_place_init,
|
||||
buffer_bytes(result->first),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{},
|
||||
result->first,
|
||||
chunk_crlf{});
|
||||
s_ = do_body_c + 2;
|
||||
BOOST_FALLTHROUGH;
|
||||
}
|
||||
|
||||
case do_body_c + 2:
|
||||
do_visit<5>(ec, visit);
|
||||
break;
|
||||
|
||||
go_body_final_c:
|
||||
s_ = do_body_final_c;
|
||||
BOOST_FALLTHROUGH;
|
||||
case do_body_final_c:
|
||||
do_visit<6>(ec, visit);
|
||||
break;
|
||||
|
||||
go_all_c:
|
||||
s_ = do_all_c;
|
||||
BOOST_FALLTHROUGH;
|
||||
case do_all_c:
|
||||
do_visit<7>(ec, visit);
|
||||
break;
|
||||
|
||||
go_final_c:
|
||||
case do_final_c:
|
||||
v_.template emplace<8>(
|
||||
boost::in_place_init,
|
||||
detail::chunk_last(),
|
||||
net::const_buffer{nullptr, 0},
|
||||
chunk_crlf{});
|
||||
s_ = do_final_c + 1;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
case do_final_c + 1:
|
||||
do_visit<8>(ec, visit);
|
||||
break;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
default:
|
||||
case do_complete:
|
||||
BOOST_ASSERT(false);
|
||||
break;
|
||||
|
||||
go_complete:
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
serializer<isRequest, Body, Fields>::
|
||||
consume(std::size_t n)
|
||||
{
|
||||
switch(s_)
|
||||
{
|
||||
case do_header:
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<2>()));
|
||||
v_.template get<2>().consume(n);
|
||||
if(buffer_bytes(v_.template get<2>()) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_.reset();
|
||||
if(! more_)
|
||||
goto go_complete;
|
||||
s_ = do_body + 1;
|
||||
break;
|
||||
|
||||
case do_header_only:
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<1>()));
|
||||
v_.template get<1>().consume(n);
|
||||
if(buffer_bytes(v_.template get<1>()) > 0)
|
||||
break;
|
||||
fwr_ = boost::none;
|
||||
header_done_ = true;
|
||||
if(! split_)
|
||||
goto go_complete;
|
||||
s_ = do_body;
|
||||
break;
|
||||
|
||||
case do_body + 2:
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<3>()));
|
||||
v_.template get<3>().consume(n);
|
||||
if(buffer_bytes(v_.template get<3>()) > 0)
|
||||
break;
|
||||
v_.reset();
|
||||
if(! more_)
|
||||
goto go_complete;
|
||||
s_ = do_body + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
case do_header_c:
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<4>()));
|
||||
v_.template get<4>().consume(n);
|
||||
if(buffer_bytes(v_.template get<4>()) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_.reset();
|
||||
if(more_)
|
||||
s_ = do_body_c + 1;
|
||||
else
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
|
||||
case do_header_only_c:
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<1>()));
|
||||
v_.template get<1>().consume(n);
|
||||
if(buffer_bytes(v_.template get<1>()) > 0)
|
||||
break;
|
||||
fwr_ = boost::none;
|
||||
header_done_ = true;
|
||||
if(! split_)
|
||||
{
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
}
|
||||
s_ = do_body_c;
|
||||
break;
|
||||
}
|
||||
|
||||
case do_body_c + 2:
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<5>()));
|
||||
v_.template get<5>().consume(n);
|
||||
if(buffer_bytes(v_.template get<5>()) > 0)
|
||||
break;
|
||||
v_.reset();
|
||||
if(more_)
|
||||
s_ = do_body_c + 1;
|
||||
else
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
|
||||
case do_body_final_c:
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<6>()));
|
||||
v_.template get<6>().consume(n);
|
||||
if(buffer_bytes(v_.template get<6>()) > 0)
|
||||
break;
|
||||
v_.reset();
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
|
||||
case do_all_c:
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
n <= buffer_bytes(v_.template get<7>()));
|
||||
v_.template get<7>().consume(n);
|
||||
if(buffer_bytes(v_.template get<7>()) > 0)
|
||||
break;
|
||||
header_done_ = true;
|
||||
v_.reset();
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
|
||||
case do_final_c + 1:
|
||||
BOOST_ASSERT(buffer_bytes(v_.template get<8>()));
|
||||
v_.template get<8>().consume(n);
|
||||
if(buffer_bytes(v_.template get<8>()) > 0)
|
||||
break;
|
||||
v_.reset();
|
||||
goto go_complete;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
default:
|
||||
BOOST_ASSERT(false);
|
||||
case do_complete:
|
||||
break;
|
||||
|
||||
go_complete:
|
||||
s_ = do_complete;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_STATUS_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_STATUS_IPP
|
||||
|
||||
#include <boost/beast/http/status.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
status
|
||||
int_to_status(unsigned v)
|
||||
{
|
||||
switch(static_cast<status>(v))
|
||||
{
|
||||
// 1xx
|
||||
case status::continue_:
|
||||
case status::switching_protocols:
|
||||
case status::processing:
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
// 2xx
|
||||
case status::ok:
|
||||
case status::created:
|
||||
case status::accepted:
|
||||
case status::non_authoritative_information:
|
||||
case status::no_content:
|
||||
case status::reset_content:
|
||||
case status::partial_content:
|
||||
case status::multi_status:
|
||||
case status::already_reported:
|
||||
case status::im_used:
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
// 3xx
|
||||
case status::multiple_choices:
|
||||
case status::moved_permanently:
|
||||
case status::found:
|
||||
case status::see_other:
|
||||
case status::not_modified:
|
||||
case status::use_proxy:
|
||||
case status::temporary_redirect:
|
||||
case status::permanent_redirect:
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
// 4xx
|
||||
case status::bad_request:
|
||||
case status::unauthorized:
|
||||
case status::payment_required:
|
||||
case status::forbidden:
|
||||
case status::not_found:
|
||||
case status::method_not_allowed:
|
||||
case status::not_acceptable:
|
||||
case status::proxy_authentication_required:
|
||||
case status::request_timeout:
|
||||
case status::conflict:
|
||||
case status::gone:
|
||||
case status::length_required:
|
||||
case status::precondition_failed:
|
||||
case status::payload_too_large:
|
||||
case status::uri_too_long:
|
||||
case status::unsupported_media_type:
|
||||
case status::range_not_satisfiable:
|
||||
case status::expectation_failed:
|
||||
case status::misdirected_request:
|
||||
case status::unprocessable_entity:
|
||||
case status::locked:
|
||||
case status::failed_dependency:
|
||||
case status::upgrade_required:
|
||||
case status::precondition_required:
|
||||
case status::too_many_requests:
|
||||
case status::request_header_fields_too_large:
|
||||
case status::connection_closed_without_response:
|
||||
case status::unavailable_for_legal_reasons:
|
||||
case status::client_closed_request:
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
// 5xx
|
||||
case status::internal_server_error:
|
||||
case status::not_implemented:
|
||||
case status::bad_gateway:
|
||||
case status::service_unavailable:
|
||||
case status::gateway_timeout:
|
||||
case status::http_version_not_supported:
|
||||
case status::variant_also_negotiates:
|
||||
case status::insufficient_storage:
|
||||
case status::loop_detected:
|
||||
case status::not_extended:
|
||||
case status::network_authentication_required:
|
||||
case status::network_connect_timeout_error:
|
||||
return static_cast<status>(v);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return status::unknown;
|
||||
}
|
||||
|
||||
status_class
|
||||
to_status_class(unsigned v)
|
||||
{
|
||||
switch(v / 100)
|
||||
{
|
||||
case 1: return status_class::informational;
|
||||
case 2: return status_class::successful;
|
||||
case 3: return status_class::redirection;
|
||||
case 4: return status_class::client_error;
|
||||
case 5: return status_class::server_error;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return status_class::unknown;
|
||||
}
|
||||
|
||||
status_class
|
||||
to_status_class(status v)
|
||||
{
|
||||
return to_status_class(static_cast<int>(v));
|
||||
}
|
||||
|
||||
string_view
|
||||
obsolete_reason(status v)
|
||||
{
|
||||
switch(static_cast<status>(v))
|
||||
{
|
||||
// 1xx
|
||||
case status::continue_: return "Continue";
|
||||
case status::switching_protocols: return "Switching Protocols";
|
||||
case status::processing: return "Processing";
|
||||
|
||||
// 2xx
|
||||
case status::ok: return "OK";
|
||||
case status::created: return "Created";
|
||||
case status::accepted: return "Accepted";
|
||||
case status::non_authoritative_information: return "Non-Authoritative Information";
|
||||
case status::no_content: return "No Content";
|
||||
case status::reset_content: return "Reset Content";
|
||||
case status::partial_content: return "Partial Content";
|
||||
case status::multi_status: return "Multi-Status";
|
||||
case status::already_reported: return "Already Reported";
|
||||
case status::im_used: return "IM Used";
|
||||
|
||||
// 3xx
|
||||
case status::multiple_choices: return "Multiple Choices";
|
||||
case status::moved_permanently: return "Moved Permanently";
|
||||
case status::found: return "Found";
|
||||
case status::see_other: return "See Other";
|
||||
case status::not_modified: return "Not Modified";
|
||||
case status::use_proxy: return "Use Proxy";
|
||||
case status::temporary_redirect: return "Temporary Redirect";
|
||||
case status::permanent_redirect: return "Permanent Redirect";
|
||||
|
||||
// 4xx
|
||||
case status::bad_request: return "Bad Request";
|
||||
case status::unauthorized: return "Unauthorized";
|
||||
case status::payment_required: return "Payment Required";
|
||||
case status::forbidden: return "Forbidden";
|
||||
case status::not_found: return "Not Found";
|
||||
case status::method_not_allowed: return "Method Not Allowed";
|
||||
case status::not_acceptable: return "Not Acceptable";
|
||||
case status::proxy_authentication_required: return "Proxy Authentication Required";
|
||||
case status::request_timeout: return "Request Timeout";
|
||||
case status::conflict: return "Conflict";
|
||||
case status::gone: return "Gone";
|
||||
case status::length_required: return "Length Required";
|
||||
case status::precondition_failed: return "Precondition Failed";
|
||||
case status::payload_too_large: return "Payload Too Large";
|
||||
case status::uri_too_long: return "URI Too Long";
|
||||
case status::unsupported_media_type: return "Unsupported Media Type";
|
||||
case status::range_not_satisfiable: return "Range Not Satisfiable";
|
||||
case status::expectation_failed: return "Expectation Failed";
|
||||
case status::misdirected_request: return "Misdirected Request";
|
||||
case status::unprocessable_entity: return "Unprocessable Entity";
|
||||
case status::locked: return "Locked";
|
||||
case status::failed_dependency: return "Failed Dependency";
|
||||
case status::upgrade_required: return "Upgrade Required";
|
||||
case status::precondition_required: return "Precondition Required";
|
||||
case status::too_many_requests: return "Too Many Requests";
|
||||
case status::request_header_fields_too_large: return "Request Header Fields Too Large";
|
||||
case status::connection_closed_without_response: return "Connection Closed Without Response";
|
||||
case status::unavailable_for_legal_reasons: return "Unavailable For Legal Reasons";
|
||||
case status::client_closed_request: return "Client Closed Request";
|
||||
// 5xx
|
||||
case status::internal_server_error: return "Internal Server Error";
|
||||
case status::not_implemented: return "Not Implemented";
|
||||
case status::bad_gateway: return "Bad Gateway";
|
||||
case status::service_unavailable: return "Service Unavailable";
|
||||
case status::gateway_timeout: return "Gateway Timeout";
|
||||
case status::http_version_not_supported: return "HTTP Version Not Supported";
|
||||
case status::variant_also_negotiates: return "Variant Also Negotiates";
|
||||
case status::insufficient_storage: return "Insufficient Storage";
|
||||
case status::loop_detected: return "Loop Detected";
|
||||
case status::not_extended: return "Not Extended";
|
||||
case status::network_authentication_required: return "Network Authentication Required";
|
||||
case status::network_connect_timeout_error: return "Network Connect Timeout Error";
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return "<unknown-status>";
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, status v)
|
||||
{
|
||||
return os << obsolete_reason(v);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_VERB_IPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_VERB_IPP
|
||||
|
||||
#include <boost/beast/http/verb.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
string_view
|
||||
to_string(verb v)
|
||||
{
|
||||
using namespace beast::detail::string_literals;
|
||||
switch(v)
|
||||
{
|
||||
case verb::delete_: return "DELETE"_sv;
|
||||
case verb::get: return "GET"_sv;
|
||||
case verb::head: return "HEAD"_sv;
|
||||
case verb::post: return "POST"_sv;
|
||||
case verb::put: return "PUT"_sv;
|
||||
case verb::connect: return "CONNECT"_sv;
|
||||
case verb::options: return "OPTIONS"_sv;
|
||||
case verb::trace: return "TRACE"_sv;
|
||||
|
||||
case verb::copy: return "COPY"_sv;
|
||||
case verb::lock: return "LOCK"_sv;
|
||||
case verb::mkcol: return "MKCOL"_sv;
|
||||
case verb::move: return "MOVE"_sv;
|
||||
case verb::propfind: return "PROPFIND"_sv;
|
||||
case verb::proppatch: return "PROPPATCH"_sv;
|
||||
case verb::search: return "SEARCH"_sv;
|
||||
case verb::unlock: return "UNLOCK"_sv;
|
||||
case verb::bind: return "BIND"_sv;
|
||||
case verb::rebind: return "REBIND"_sv;
|
||||
case verb::unbind: return "UNBIND"_sv;
|
||||
case verb::acl: return "ACL"_sv;
|
||||
|
||||
case verb::report: return "REPORT"_sv;
|
||||
case verb::mkactivity: return "MKACTIVITY"_sv;
|
||||
case verb::checkout: return "CHECKOUT"_sv;
|
||||
case verb::merge: return "MERGE"_sv;
|
||||
|
||||
case verb::msearch: return "M-SEARCH"_sv;
|
||||
case verb::notify: return "NOTIFY"_sv;
|
||||
case verb::subscribe: return "SUBSCRIBE"_sv;
|
||||
case verb::unsubscribe: return "UNSUBSCRIBE"_sv;
|
||||
|
||||
case verb::patch: return "PATCH"_sv;
|
||||
case verb::purge: return "PURGE"_sv;
|
||||
|
||||
case verb::mkcalendar: return "MKCALENDAR"_sv;
|
||||
|
||||
case verb::link: return "LINK"_sv;
|
||||
case verb::unlink: return "UNLINK"_sv;
|
||||
|
||||
case verb::unknown:
|
||||
return "<unknown>"_sv;
|
||||
}
|
||||
|
||||
BOOST_THROW_EXCEPTION(std::invalid_argument{"unknown verb"});
|
||||
}
|
||||
|
||||
verb
|
||||
string_to_verb(string_view v)
|
||||
{
|
||||
/*
|
||||
ACL
|
||||
BIND
|
||||
CHECKOUT
|
||||
CONNECT
|
||||
COPY
|
||||
DELETE
|
||||
GET
|
||||
HEAD
|
||||
LINK
|
||||
LOCK
|
||||
M-SEARCH
|
||||
MERGE
|
||||
MKACTIVITY
|
||||
MKCALENDAR
|
||||
MKCOL
|
||||
MOVE
|
||||
NOTIFY
|
||||
OPTIONS
|
||||
PATCH
|
||||
POST
|
||||
PROPFIND
|
||||
PROPPATCH
|
||||
PURGE
|
||||
PUT
|
||||
REBIND
|
||||
REPORT
|
||||
SEARCH
|
||||
SUBSCRIBE
|
||||
TRACE
|
||||
UNBIND
|
||||
UNLINK
|
||||
UNLOCK
|
||||
UNSUBSCRIBE
|
||||
*/
|
||||
using namespace beast::detail::string_literals;
|
||||
if(v.size() < 3)
|
||||
return verb::unknown;
|
||||
auto c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case 'A':
|
||||
if(v == "CL"_sv)
|
||||
return verb::acl;
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
if(v == "IND"_sv)
|
||||
return verb::bind;
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case 'H':
|
||||
if(v == "ECKOUT"_sv)
|
||||
return verb::checkout;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(v == "NNECT"_sv)
|
||||
return verb::connect;
|
||||
if(v == "PY"_sv)
|
||||
return verb::copy;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
if(v == "ELETE"_sv)
|
||||
return verb::delete_;
|
||||
break;
|
||||
|
||||
case 'G':
|
||||
if(v == "ET"_sv)
|
||||
return verb::get;
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
if(v == "EAD"_sv)
|
||||
return verb::head;
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
if(v == "INK"_sv)
|
||||
return verb::link;
|
||||
if(v == "OCK"_sv)
|
||||
return verb::lock;
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case '-':
|
||||
if(v == "SEARCH"_sv)
|
||||
return verb::msearch;
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
if(v == "RGE"_sv)
|
||||
return verb::merge;
|
||||
break;
|
||||
|
||||
case 'K':
|
||||
if(v == "ACTIVITY"_sv)
|
||||
return verb::mkactivity;
|
||||
if(v[0] == 'C')
|
||||
{
|
||||
v.remove_prefix(1);
|
||||
if(v == "ALENDAR"_sv)
|
||||
return verb::mkcalendar;
|
||||
if(v == "OL"_sv)
|
||||
return verb::mkcol;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(v == "VE"_sv)
|
||||
return verb::move;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'N':
|
||||
if(v == "OTIFY"_sv)
|
||||
return verb::notify;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(v == "PTIONS"_sv)
|
||||
return verb::options;
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
c = v[0];
|
||||
v.remove_prefix(1);
|
||||
switch(c)
|
||||
{
|
||||
case 'A':
|
||||
if(v == "TCH"_sv)
|
||||
return verb::patch;
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
if(v == "ST"_sv)
|
||||
return verb::post;
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
if(v == "OPFIND"_sv)
|
||||
return verb::propfind;
|
||||
if(v == "OPPATCH"_sv)
|
||||
return verb::proppatch;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
if(v == "RGE"_sv)
|
||||
return verb::purge;
|
||||
if(v == "T"_sv)
|
||||
return verb::put;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'R':
|
||||
if(v[0] != 'E')
|
||||
break;
|
||||
v.remove_prefix(1);
|
||||
if(v == "BIND"_sv)
|
||||
return verb::rebind;
|
||||
if(v == "PORT"_sv)
|
||||
return verb::report;
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
if(v == "EARCH"_sv)
|
||||
return verb::search;
|
||||
if(v == "UBSCRIBE"_sv)
|
||||
return verb::subscribe;
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
if(v == "RACE"_sv)
|
||||
return verb::trace;
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
if(v[0] != 'N')
|
||||
break;
|
||||
v.remove_prefix(1);
|
||||
if(v == "BIND"_sv)
|
||||
return verb::unbind;
|
||||
if(v == "LINK"_sv)
|
||||
return verb::unlink;
|
||||
if(v == "LOCK"_sv)
|
||||
return verb::unlock;
|
||||
if(v == "SUBSCRIBE"_sv)
|
||||
return verb::unsubscribe;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return verb::unknown;
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+997
@@ -0,0 +1,997 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_IMPL_WRITE_HPP
|
||||
#define BOOST_BEAST_HTTP_IMPL_WRITE_HPP
|
||||
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/beast/core/async_base.hpp>
|
||||
#include <boost/beast/core/bind_handler.hpp>
|
||||
#include <boost/beast/core/buffers_range.hpp>
|
||||
#include <boost/beast/core/make_printable.hpp>
|
||||
#include <boost/beast/core/stream_traits.hpp>
|
||||
#include <boost/beast/core/detail/is_invocable.hpp>
|
||||
#include <boost/asio/coroutine.hpp>
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<
|
||||
class Handler,
|
||||
class Stream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_some_op
|
||||
: public beast::async_base<
|
||||
Handler, beast::executor_type<Stream>>
|
||||
{
|
||||
Stream& s_;
|
||||
serializer<isRequest,Body, Fields>& sr_;
|
||||
|
||||
class lambda
|
||||
{
|
||||
write_some_op& op_;
|
||||
|
||||
public:
|
||||
bool invoked = false;
|
||||
|
||||
explicit
|
||||
lambda(write_some_op& op)
|
||||
: op_(op)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(
|
||||
error_code& ec,
|
||||
ConstBufferSequence const& buffers)
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_write_some"));
|
||||
|
||||
invoked = true;
|
||||
ec = {};
|
||||
op_.s_.async_write_some(
|
||||
buffers, std::move(op_));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
template<class Handler_>
|
||||
write_some_op(
|
||||
Handler_&& h,
|
||||
Stream& s,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
: async_base<
|
||||
Handler, beast::executor_type<Stream>>(
|
||||
std::forward<Handler_>(h), s.get_executor())
|
||||
, s_(s)
|
||||
, sr_(sr)
|
||||
{
|
||||
(*this)();
|
||||
}
|
||||
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
error_code ec;
|
||||
if(! sr_.is_done())
|
||||
{
|
||||
lambda f{*this};
|
||||
sr_.next(ec, f);
|
||||
if(ec)
|
||||
{
|
||||
BOOST_ASSERT(! f.invoked);
|
||||
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_write_some"));
|
||||
|
||||
auto ex = asio::get_associated_immediate_executor(*this, s_.get_executor());
|
||||
return net::dispatch(
|
||||
ex,
|
||||
beast::bind_front_handler(
|
||||
std::move(*this), ec, 0));
|
||||
}
|
||||
if(f.invoked)
|
||||
{
|
||||
// *this is now moved-from,
|
||||
return;
|
||||
}
|
||||
// What else could it be?
|
||||
BOOST_ASSERT(sr_.is_done());
|
||||
}
|
||||
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_write_some"));
|
||||
|
||||
const auto ex = this->get_immediate_executor();
|
||||
return net::dispatch(
|
||||
ex,
|
||||
beast::bind_front_handler(
|
||||
std::move(*this), ec, 0));
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
if(! ec)
|
||||
sr_.consume(bytes_transferred);
|
||||
this->complete_now(ec, bytes_transferred);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct serializer_is_header_done
|
||||
{
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
bool
|
||||
operator()(
|
||||
serializer<isRequest, Body, Fields>& sr) const
|
||||
{
|
||||
return sr.is_header_done();
|
||||
}
|
||||
};
|
||||
|
||||
struct serializer_is_done
|
||||
{
|
||||
template<
|
||||
bool isRequest, class Body, class Fields>
|
||||
bool
|
||||
operator()(
|
||||
serializer<isRequest, Body, Fields>& sr) const
|
||||
{
|
||||
return sr.is_done();
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class Handler,
|
||||
class Stream,
|
||||
class Predicate,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_op
|
||||
: public beast::async_base<
|
||||
Handler, beast::executor_type<Stream>>
|
||||
, public asio::coroutine
|
||||
{
|
||||
Stream& s_;
|
||||
serializer<isRequest, Body, Fields>& sr_;
|
||||
std::size_t bytes_transferred_ = 0;
|
||||
net::cancellation_state st_{this->
|
||||
beast::async_base<Handler, beast::executor_type<Stream>>
|
||||
::get_cancellation_slot()};
|
||||
|
||||
public:
|
||||
using cancellation_slot_type = net::cancellation_slot;
|
||||
cancellation_slot_type get_cancellation_slot() const noexcept
|
||||
{
|
||||
return st_.slot();
|
||||
}
|
||||
|
||||
|
||||
template<class Handler_>
|
||||
write_op(
|
||||
Handler_&& h,
|
||||
Stream& s,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
: async_base<
|
||||
Handler, beast::executor_type<Stream>>(
|
||||
std::forward<Handler_>(h), s.get_executor())
|
||||
, s_(s)
|
||||
, sr_(sr)
|
||||
{
|
||||
(*this)();
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec = {},
|
||||
std::size_t bytes_transferred = 0)
|
||||
{
|
||||
BOOST_ASIO_CORO_REENTER(*this)
|
||||
{
|
||||
if(Predicate{}(sr_))
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_write"));
|
||||
|
||||
const auto ex = this->get_immediate_executor();
|
||||
net::dispatch(
|
||||
ex,
|
||||
std::move(*this));
|
||||
}
|
||||
goto upcall;
|
||||
}
|
||||
for(;;)
|
||||
{
|
||||
BOOST_ASIO_CORO_YIELD
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_write"));
|
||||
|
||||
beast::http::async_write_some(
|
||||
s_, sr_, std::move(*this));
|
||||
}
|
||||
bytes_transferred_ += bytes_transferred;
|
||||
if (!ec && st_.cancelled() != net::cancellation_type::none)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, net::error::operation_aborted);
|
||||
}
|
||||
if(ec)
|
||||
goto upcall;
|
||||
if(Predicate{}(sr_))
|
||||
break;
|
||||
}
|
||||
upcall:
|
||||
this->complete_now(ec, bytes_transferred_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class Handler,
|
||||
class Stream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_msg_op
|
||||
: public beast::stable_async_base<
|
||||
Handler, beast::executor_type<Stream>>
|
||||
{
|
||||
Stream& s_;
|
||||
serializer<isRequest, Body, Fields>& sr_;
|
||||
|
||||
public:
|
||||
template<
|
||||
class Handler_,
|
||||
class... Args>
|
||||
write_msg_op(
|
||||
Handler_&& h,
|
||||
Stream& s,
|
||||
Args&&... args)
|
||||
: stable_async_base<
|
||||
Handler, beast::executor_type<Stream>>(
|
||||
std::forward<Handler_>(h), s.get_executor())
|
||||
, s_(s)
|
||||
, sr_(beast::allocate_stable<
|
||||
serializer<isRequest, Body, Fields>>(
|
||||
*this, std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)();
|
||||
}
|
||||
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
BOOST_ASIO_HANDLER_LOCATION((
|
||||
__FILE__, __LINE__,
|
||||
"http::async_write(msg)"));
|
||||
|
||||
async_write(s_, sr_, std::move(*this));
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
this->complete_now(ec, bytes_transferred);
|
||||
}
|
||||
};
|
||||
|
||||
struct run_write_some_op
|
||||
{
|
||||
template<
|
||||
class WriteHandler,
|
||||
class Stream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
operator()(
|
||||
WriteHandler&& h,
|
||||
Stream* s,
|
||||
serializer<isRequest, Body, Fields>* sr)
|
||||
{
|
||||
// If you get an error on the following line it means
|
||||
// that your handler does not meet the documented type
|
||||
// requirements for the handler.
|
||||
|
||||
static_assert(
|
||||
beast::detail::is_invocable<WriteHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"WriteHandler type requirements not met");
|
||||
|
||||
write_some_op<
|
||||
typename std::decay<WriteHandler>::type,
|
||||
Stream,
|
||||
isRequest, Body, Fields>(
|
||||
std::forward<WriteHandler>(h), *s, *sr);
|
||||
}
|
||||
};
|
||||
|
||||
struct run_write_op
|
||||
{
|
||||
template<
|
||||
class WriteHandler,
|
||||
class Stream,
|
||||
class Predicate,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
operator()(
|
||||
WriteHandler&& h,
|
||||
Stream* s,
|
||||
Predicate const&,
|
||||
serializer<isRequest, Body, Fields>* sr)
|
||||
{
|
||||
// If you get an error on the following line it means
|
||||
// that your handler does not meet the documented type
|
||||
// requirements for the handler.
|
||||
|
||||
static_assert(
|
||||
beast::detail::is_invocable<WriteHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"WriteHandler type requirements not met");
|
||||
|
||||
write_op<
|
||||
typename std::decay<WriteHandler>::type,
|
||||
Stream,
|
||||
Predicate,
|
||||
isRequest, Body, Fields>(
|
||||
std::forward<WriteHandler>(h), *s, *sr);
|
||||
}
|
||||
};
|
||||
|
||||
struct run_write_msg_op
|
||||
{
|
||||
template<
|
||||
class WriteHandler,
|
||||
class Stream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class... Args>
|
||||
void
|
||||
operator()(
|
||||
WriteHandler&& h,
|
||||
Stream* s,
|
||||
message<isRequest, Body, Fields>* m,
|
||||
std::false_type,
|
||||
Args&&... args)
|
||||
{
|
||||
// If you get an error on the following line it means
|
||||
// that your handler does not meet the documented type
|
||||
// requirements for the handler.
|
||||
|
||||
static_assert(
|
||||
beast::detail::is_invocable<WriteHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"WriteHandler type requirements not met");
|
||||
|
||||
write_msg_op<
|
||||
typename std::decay<WriteHandler>::type,
|
||||
Stream,
|
||||
isRequest, Body, Fields>(
|
||||
std::forward<WriteHandler>(h), *s, *m,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<
|
||||
class WriteHandler,
|
||||
class Stream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class... Args>
|
||||
void
|
||||
operator()(
|
||||
WriteHandler&& h,
|
||||
Stream* s,
|
||||
message<isRequest, Body, Fields> const* m,
|
||||
std::true_type,
|
||||
Args&&... args)
|
||||
{
|
||||
// If you get an error on the following line it means
|
||||
// that your handler does not meet the documented type
|
||||
// requirements for the handler.
|
||||
|
||||
static_assert(
|
||||
beast::detail::is_invocable<WriteHandler,
|
||||
void(error_code, std::size_t)>::value,
|
||||
"WriteHandler type requirements not met");
|
||||
|
||||
write_msg_op<
|
||||
typename std::decay<WriteHandler>::type,
|
||||
Stream,
|
||||
isRequest, Body, Fields>(
|
||||
std::forward<WriteHandler>(h), *s, *m,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream>
|
||||
class write_some_lambda
|
||||
{
|
||||
Stream& stream_;
|
||||
|
||||
public:
|
||||
bool invoked = false;
|
||||
std::size_t bytes_transferred = 0;
|
||||
|
||||
explicit
|
||||
write_some_lambda(Stream& stream)
|
||||
: stream_(stream)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffers)
|
||||
{
|
||||
invoked = true;
|
||||
bytes_transferred =
|
||||
stream_.write_some(buffers, ec);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream>
|
||||
class write_lambda
|
||||
{
|
||||
Stream& stream_;
|
||||
|
||||
public:
|
||||
bool invoked = false;
|
||||
std::size_t bytes_transferred = 0;
|
||||
|
||||
explicit
|
||||
write_lambda(Stream& stream)
|
||||
: stream_(stream)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffers)
|
||||
{
|
||||
invoked = true;
|
||||
bytes_transferred = net::write(
|
||||
stream_, buffers, ec);
|
||||
}
|
||||
};
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_some_impl(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
if(! sr.is_done())
|
||||
{
|
||||
write_some_lambda<SyncWriteStream> f{stream};
|
||||
sr.next(ec, f);
|
||||
if(ec)
|
||||
return f.bytes_transferred;
|
||||
if(f.invoked)
|
||||
sr.consume(f.bytes_transferred);
|
||||
return f.bytes_transferred;
|
||||
}
|
||||
ec = {};
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write_some_impl(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
return net::async_initiate<
|
||||
WriteHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
run_write_some_op{},
|
||||
handler,
|
||||
&stream,
|
||||
&sr);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_some(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
write_some(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_some(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
return detail::write_some_impl(stream, sr, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write_some(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
return detail::async_write_some_impl(stream, sr,
|
||||
std::forward<WriteHandler>(handler));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_header(SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
write_header(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_header(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
sr.split(true);
|
||||
std::size_t bytes_transferred = 0;
|
||||
if(! sr.is_header_done())
|
||||
{
|
||||
detail::write_lambda<SyncWriteStream> f{stream};
|
||||
do
|
||||
{
|
||||
sr.next(ec, f);
|
||||
bytes_transferred += f.bytes_transferred;
|
||||
if(ec)
|
||||
return bytes_transferred;
|
||||
BOOST_ASSERT(f.invoked);
|
||||
sr.consume(f.bytes_transferred);
|
||||
}
|
||||
while(! sr.is_header_done());
|
||||
}
|
||||
else
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write_header(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
sr.split(true);
|
||||
return net::async_initiate<
|
||||
WriteHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::run_write_op{},
|
||||
handler,
|
||||
&stream,
|
||||
detail::serializer_is_header_done{},
|
||||
&sr);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
write(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
std::size_t bytes_transferred = 0;
|
||||
sr.split(false);
|
||||
for(;;)
|
||||
{
|
||||
bytes_transferred +=
|
||||
write_some(stream, sr, ec);
|
||||
if(ec)
|
||||
return bytes_transferred;
|
||||
if(sr.is_done())
|
||||
break;
|
||||
}
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
sr.split(false);
|
||||
return net::async_initiate<
|
||||
WriteHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::run_write_op{},
|
||||
handler,
|
||||
&stream,
|
||||
detail::serializer_is_done{},
|
||||
&sr);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
write(stream, msg, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
write(stream, msg, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return bytes_transferred;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
serializer<isRequest, Body, Fields> sr{msg};
|
||||
return write(stream, sr, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
serializer<isRequest, Body, Fields> sr{msg};
|
||||
return write(stream, sr, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
WriteHandler&& handler,
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value>::type*)
|
||||
{
|
||||
static_assert(
|
||||
is_async_write_stream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
return net::async_initiate<
|
||||
WriteHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::run_write_msg_op{},
|
||||
handler,
|
||||
&stream,
|
||||
&msg,
|
||||
std::false_type{});
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
WriteHandler&& handler,
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value>::type*)
|
||||
{
|
||||
static_assert(
|
||||
is_async_write_stream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream type requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
return net::async_initiate<
|
||||
WriteHandler,
|
||||
void(error_code, std::size_t)>(
|
||||
detail::run_write_msg_op{},
|
||||
handler,
|
||||
&stream,
|
||||
&msg,
|
||||
std::true_type{});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Serializer>
|
||||
class write_ostream_lambda
|
||||
{
|
||||
std::ostream& os_;
|
||||
Serializer& sr_;
|
||||
|
||||
public:
|
||||
write_ostream_lambda(std::ostream& os,
|
||||
Serializer& sr)
|
||||
: os_(os)
|
||||
, sr_(sr)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffers) const
|
||||
{
|
||||
ec = {};
|
||||
if(os_.fail())
|
||||
return;
|
||||
std::size_t bytes_transferred = 0;
|
||||
for(auto b : beast::buffers_range_ref(buffers))
|
||||
{
|
||||
os_.write(static_cast<char const*>(
|
||||
b.data()), b.size());
|
||||
if(os_.fail())
|
||||
return;
|
||||
bytes_transferred += b.size();
|
||||
}
|
||||
sr_.consume(bytes_transferred);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
template<class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
header<true, Fields> const& h)
|
||||
{
|
||||
typename Fields::writer fr{
|
||||
h, h.version(), h.method()};
|
||||
return os << beast::make_printable(fr.get());
|
||||
}
|
||||
|
||||
template<class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
header<false, Fields> const& h)
|
||||
{
|
||||
typename Fields::writer fr{
|
||||
h, h.version(), h.result_int()};
|
||||
return os << beast::make_printable(fr.get());
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
message<isRequest, Body, Fields> const& msg)
|
||||
{
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
serializer<isRequest, Body, Fields> sr{msg};
|
||||
error_code ec;
|
||||
detail::write_ostream_lambda<decltype(sr)> f{os, sr};
|
||||
do
|
||||
{
|
||||
sr.next(ec, f);
|
||||
if(os.fail())
|
||||
break;
|
||||
if(ec)
|
||||
{
|
||||
os.setstate(std::ios::failbit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(! sr.is_done());
|
||||
return os;
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+1008
File diff suppressed because it is too large
Load Diff
+99
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// Copyright (c) 2022 Seth Heeren (sgheeren 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
#ifndef BOOST_BEAST_HTTP_MESSAGE_GENERATOR_HPP
|
||||
#define BOOST_BEAST_HTTP_MESSAGE_GENERATOR_HPP
|
||||
|
||||
#include <boost/beast/core/span.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/serializer.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Type-erased buffers generator for @ref http::message
|
||||
|
||||
Implements the BuffersGenerator concept for any concrete instance of the
|
||||
@ref http::message template.
|
||||
|
||||
@ref http::message_generator takes ownership of a message on construction,
|
||||
erasing the concrete type from the interface.
|
||||
|
||||
This makes it practical for use in server applications to implement request
|
||||
handling:
|
||||
|
||||
@code
|
||||
template <class Body, class Fields>
|
||||
http::message_generator handle_request(
|
||||
string_view doc_root,
|
||||
http::request<Body, Fields>&& request);
|
||||
@endcode
|
||||
|
||||
The @ref beast::write and @ref beast::async_write operations are provided
|
||||
for BuffersGenerator. The @ref http::message::keep_alive property is made
|
||||
available for use after writing the message.
|
||||
*/
|
||||
class message_generator
|
||||
{
|
||||
public:
|
||||
using const_buffers_type = span<net::const_buffer>;
|
||||
|
||||
template <bool isRequest, class Body, class Fields>
|
||||
message_generator(http::message<isRequest, Body, Fields>&&);
|
||||
|
||||
/// `BuffersGenerator`
|
||||
bool is_done() const {
|
||||
return impl_->is_done();
|
||||
}
|
||||
|
||||
/// `BuffersGenerator`
|
||||
const_buffers_type
|
||||
prepare(error_code& ec)
|
||||
{
|
||||
return impl_->prepare(ec);
|
||||
}
|
||||
|
||||
/// `BuffersGenerator`
|
||||
void
|
||||
consume(std::size_t n)
|
||||
{
|
||||
impl_->consume(n);
|
||||
}
|
||||
|
||||
/// Returns the result of `m.keep_alive()` on the underlying message
|
||||
bool
|
||||
keep_alive() const noexcept
|
||||
{
|
||||
return impl_->keep_alive();
|
||||
}
|
||||
|
||||
private:
|
||||
struct impl_base
|
||||
{
|
||||
virtual ~impl_base() = default;
|
||||
virtual bool is_done() = 0;
|
||||
virtual const_buffers_type prepare(error_code& ec) = 0;
|
||||
virtual void consume(std::size_t n) = 0;
|
||||
virtual bool keep_alive() const noexcept = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<impl_base> impl_;
|
||||
|
||||
template <bool isRequest, class Body, class Fields>
|
||||
struct generator_impl;
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace beast
|
||||
} // namespace boost
|
||||
|
||||
#include <boost/beast/http/impl/message_generator.hpp>
|
||||
|
||||
#endif
|
||||
+506
@@ -0,0 +1,506 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_PARSER_HPP
|
||||
#define BOOST_BEAST_HTTP_PARSER_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/http/basic_parser.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** An HTTP/1 parser for producing a message.
|
||||
|
||||
This class uses the basic HTTP/1 wire format parser to convert
|
||||
a series of octets into a @ref message using the @ref basic_fields
|
||||
container to represent the fields.
|
||||
|
||||
@tparam isRequest Indicates whether a request or response
|
||||
will be parsed.
|
||||
|
||||
@tparam Body The type used to represent the body. This must
|
||||
meet the requirements of <em>Body</em>.
|
||||
|
||||
@tparam Allocator The type of allocator used with the
|
||||
@ref basic_fields container.
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
*/
|
||||
template<
|
||||
bool isRequest,
|
||||
class Body,
|
||||
class Allocator = std::allocator<char>>
|
||||
class parser
|
||||
: public basic_parser<isRequest>
|
||||
{
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader type requirements not met");
|
||||
|
||||
template<bool, class, class>
|
||||
friend class parser;
|
||||
|
||||
message<isRequest, Body, basic_fields<Allocator>> m_;
|
||||
typename Body::reader rd_;
|
||||
bool rd_inited_ = false;
|
||||
bool used_ = false;
|
||||
|
||||
std::function<void(
|
||||
std::uint64_t,
|
||||
string_view,
|
||||
error_code&)> cb_h_;
|
||||
|
||||
std::function<std::size_t(
|
||||
std::uint64_t,
|
||||
string_view,
|
||||
error_code&)> cb_b_;
|
||||
|
||||
public:
|
||||
/// The type of message returned by the parser
|
||||
using value_type =
|
||||
message<isRequest, Body, basic_fields<Allocator>>;
|
||||
|
||||
/// Destructor
|
||||
~parser() = default;
|
||||
|
||||
/// Constructor (disallowed)
|
||||
parser(parser const&) = delete;
|
||||
|
||||
/// Assignment (disallowed)
|
||||
parser& operator=(parser const&) = delete;
|
||||
|
||||
/// Constructor (disallowed)
|
||||
parser(parser&& other) = delete;
|
||||
|
||||
/// Constructor
|
||||
parser();
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param args Optional arguments forwarded to the
|
||||
@ref http::message constructor.
|
||||
|
||||
@note This function participates in overload
|
||||
resolution only if the first argument is not a
|
||||
@ref parser.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class... Args>
|
||||
explicit
|
||||
parser(Args&&... args);
|
||||
#else
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<
|
||||
! detail::is_parser<typename
|
||||
std::decay<Arg1>::type>::value>::type>
|
||||
explicit
|
||||
parser(Arg1&& arg1, ArgN&&... argn);
|
||||
#endif
|
||||
|
||||
/** Construct a parser from another parser, changing the Body type.
|
||||
|
||||
This constructs a new parser by move constructing the
|
||||
header from another parser with a different body type. The
|
||||
constructed-from parser must not have any parsed body octets or
|
||||
initialized <em>BodyReader</em>, otherwise an exception is generated.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
// Deferred body type commitment
|
||||
request_parser<empty_body> req0;
|
||||
...
|
||||
request_parser<string_body> req{std::move(req0)};
|
||||
@endcode
|
||||
|
||||
If an exception is thrown, the state of the constructed-from
|
||||
parser is undefined.
|
||||
|
||||
@param parser The other parser to construct from. After
|
||||
this call returns, the constructed-from parser may only
|
||||
be destroyed.
|
||||
|
||||
@param args Optional arguments forwarded to the message
|
||||
constructor.
|
||||
|
||||
@throws std::invalid_argument Thrown when the constructed-from
|
||||
parser has already initialized a body reader.
|
||||
|
||||
@note This function participates in overload resolution only
|
||||
if the other parser uses a different body type.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class OtherBody, class... Args>
|
||||
#else
|
||||
template<class OtherBody, class... Args,
|
||||
class = typename std::enable_if<
|
||||
! std::is_same<Body, OtherBody>::value>::type>
|
||||
#endif
|
||||
explicit
|
||||
parser(parser<isRequest, OtherBody,
|
||||
Allocator>&& parser, Args&&... args);
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Depending on the parser's progress,
|
||||
parts of this object may be incomplete.
|
||||
*/
|
||||
value_type const&
|
||||
get() const
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Depending on the parser's progress,
|
||||
parts of this object may be incomplete.
|
||||
*/
|
||||
value_type&
|
||||
get()
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/** Returns ownership of the parsed message.
|
||||
|
||||
Ownership is transferred to the caller.
|
||||
Depending on the parser's progress,
|
||||
parts of this object may be incomplete.
|
||||
|
||||
@par Requires
|
||||
|
||||
@ref value_type is @b MoveConstructible
|
||||
*/
|
||||
value_type
|
||||
release()
|
||||
{
|
||||
static_assert(std::is_move_constructible<decltype(m_)>::value,
|
||||
"MoveConstructible requirements not met");
|
||||
return std::move(m_);
|
||||
}
|
||||
|
||||
/** Set a callback to be invoked on each chunk header.
|
||||
|
||||
The callback will be invoked once for every chunk in the message
|
||||
payload, as well as once for the last chunk. The invocation
|
||||
happens after the chunk header is available but before any body
|
||||
octets have been parsed.
|
||||
|
||||
The extensions are provided in raw, validated form, use
|
||||
@ref chunk_extensions::parse to parse the extensions into a
|
||||
structured container for easier access.
|
||||
The implementation type-erases the callback without requiring
|
||||
a dynamic allocation. For this reason, the callback object is
|
||||
passed by a non-constant reference.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
auto callback =
|
||||
[](std::uint64_t size, string_view extensions, error_code& ec)
|
||||
{
|
||||
//...
|
||||
};
|
||||
parser.on_chunk_header(callback);
|
||||
@endcode
|
||||
|
||||
@param cb The function to set, which must be invocable with
|
||||
this equivalent signature:
|
||||
@code
|
||||
void
|
||||
on_chunk_header(
|
||||
std::uint64_t size, // Size of the chunk, zero for the last chunk
|
||||
string_view extensions, // The chunk-extensions in raw form
|
||||
error_code& ec); // May be set by the callback to indicate an error
|
||||
@endcode
|
||||
*/
|
||||
template<class Callback>
|
||||
void
|
||||
on_chunk_header(Callback& cb)
|
||||
{
|
||||
// Callback may not be constant, caller is responsible for
|
||||
// managing the lifetime of the callback. Copies are not made.
|
||||
BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
|
||||
|
||||
// Can't set the callback after receiving any chunk data!
|
||||
BOOST_ASSERT(! rd_inited_);
|
||||
|
||||
cb_h_ = std::ref(cb);
|
||||
}
|
||||
|
||||
/** Set a callback to be invoked on chunk body data
|
||||
|
||||
The provided function object will be invoked one or more times
|
||||
to provide buffers corresponding to the chunk body for the current
|
||||
chunk. The callback receives the number of octets remaining in this
|
||||
chunk body including the octets in the buffer provided.
|
||||
|
||||
The callback must return the number of octets actually consumed.
|
||||
Any octets not consumed will be presented again in a subsequent
|
||||
invocation of the callback.
|
||||
The implementation type-erases the callback without requiring
|
||||
a dynamic allocation. For this reason, the callback object is
|
||||
passed by a non-constant reference.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
auto callback =
|
||||
[](std::uint64_t remain, string_view body, error_code& ec)
|
||||
{
|
||||
//...
|
||||
};
|
||||
parser.on_chunk_body(callback);
|
||||
@endcode
|
||||
|
||||
@param cb The function to set, which must be invocable with
|
||||
this equivalent signature:
|
||||
@code
|
||||
std::size_t
|
||||
on_chunk_header(
|
||||
std::uint64_t remain, // Octets remaining in this chunk, includes `body`
|
||||
string_view body, // A buffer holding some or all of the remainder of the chunk body
|
||||
error_code& ec); // May be set by the callback to indicate an error
|
||||
@endcode
|
||||
*/
|
||||
template<class Callback>
|
||||
void
|
||||
on_chunk_body(Callback& cb)
|
||||
{
|
||||
// Callback may not be constant, caller is responsible for
|
||||
// managing the lifetime of the callback. Copies are not made.
|
||||
BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
|
||||
|
||||
// Can't set the callback after receiving any chunk data!
|
||||
BOOST_ASSERT(! rd_inited_);
|
||||
|
||||
cb_b_ = std::ref(cb);
|
||||
}
|
||||
|
||||
private:
|
||||
parser(std::true_type);
|
||||
parser(std::false_type);
|
||||
|
||||
template<class OtherBody, class... Args,
|
||||
class = typename std::enable_if<
|
||||
! std::is_same<Body, OtherBody>::value>::type>
|
||||
parser(
|
||||
std::true_type,
|
||||
parser<isRequest, OtherBody, Allocator>&& parser,
|
||||
Args&&... args);
|
||||
|
||||
template<class OtherBody, class... Args,
|
||||
class = typename std::enable_if<
|
||||
! std::is_same<Body, OtherBody>::value>::type>
|
||||
parser(
|
||||
std::false_type,
|
||||
parser<isRequest, OtherBody, Allocator>&& parser,
|
||||
Args&&... args);
|
||||
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<
|
||||
! detail::is_parser<typename
|
||||
std::decay<Arg1>::type>::value>::type>
|
||||
explicit
|
||||
parser(Arg1&& arg1, std::true_type, ArgN&&... argn);
|
||||
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<
|
||||
! detail::is_parser<typename
|
||||
std::decay<Arg1>::type>::value>::type>
|
||||
explicit
|
||||
parser(Arg1&& arg1, std::false_type, ArgN&&... argn);
|
||||
|
||||
void
|
||||
on_request_impl(
|
||||
verb method,
|
||||
string_view method_str,
|
||||
string_view target,
|
||||
int version,
|
||||
error_code& ec,
|
||||
std::true_type)
|
||||
{
|
||||
// If this assert goes off, it means you tried to re-use a
|
||||
// parser after it was done reading a message. This is not
|
||||
// allowed, you need to create a new parser for each message.
|
||||
// The easiest way to do that is to store the parser in
|
||||
// an optional object.
|
||||
|
||||
BOOST_ASSERT(! used_);
|
||||
if(used_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::stale_parser);
|
||||
return;
|
||||
}
|
||||
used_ = true;
|
||||
|
||||
m_.target(target);
|
||||
if(method != verb::unknown)
|
||||
m_.method(method);
|
||||
else
|
||||
m_.method_string(method_str);
|
||||
m_.version(version);
|
||||
}
|
||||
|
||||
void
|
||||
on_request_impl(
|
||||
verb, string_view, string_view,
|
||||
int, error_code&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_request_impl(
|
||||
verb method,
|
||||
string_view method_str,
|
||||
string_view target,
|
||||
int version,
|
||||
error_code& ec) override
|
||||
{
|
||||
this->on_request_impl(
|
||||
method, method_str, target, version, ec,
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void
|
||||
on_response_impl(
|
||||
int code,
|
||||
string_view reason,
|
||||
int version,
|
||||
error_code& ec,
|
||||
std::true_type)
|
||||
{
|
||||
// If this assert goes off, it means you tried to re-use a
|
||||
// parser after it was done reading a message. This is not
|
||||
// allowed, you need to create a new parser for each message.
|
||||
// The easiest way to do that is to store the parser in
|
||||
// an optional object.
|
||||
|
||||
BOOST_ASSERT(! used_);
|
||||
if(used_)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::stale_parser);
|
||||
return;
|
||||
}
|
||||
used_ = true;
|
||||
|
||||
m_.result(code);
|
||||
m_.version(version);
|
||||
m_.reason(reason);
|
||||
}
|
||||
|
||||
void
|
||||
on_response_impl(
|
||||
int, string_view, int,
|
||||
error_code&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_response_impl(
|
||||
int code,
|
||||
string_view reason,
|
||||
int version,
|
||||
error_code& ec) override
|
||||
{
|
||||
this->on_response_impl(
|
||||
code, reason, version, ec,
|
||||
std::integral_constant<bool, ! isRequest>{});
|
||||
}
|
||||
|
||||
void
|
||||
on_field_impl(
|
||||
field name,
|
||||
string_view name_string,
|
||||
string_view value,
|
||||
error_code&) override
|
||||
{
|
||||
m_.insert(name, name_string, value);
|
||||
}
|
||||
|
||||
void
|
||||
on_header_impl(error_code& ec) override
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
|
||||
void
|
||||
on_body_init_impl(
|
||||
boost::optional<std::uint64_t> const& content_length,
|
||||
error_code& ec) override
|
||||
{
|
||||
rd_.init(content_length, ec);
|
||||
rd_inited_ = true;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
on_body_impl(
|
||||
string_view body,
|
||||
error_code& ec) override
|
||||
{
|
||||
return rd_.put(net::buffer(
|
||||
body.data(), body.size()), ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_chunk_header_impl(
|
||||
std::uint64_t size,
|
||||
string_view extensions,
|
||||
error_code& ec) override
|
||||
{
|
||||
if(cb_h_)
|
||||
return cb_h_(size, extensions, ec);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
on_chunk_body_impl(
|
||||
std::uint64_t remain,
|
||||
string_view body,
|
||||
error_code& ec) override
|
||||
{
|
||||
if(cb_b_)
|
||||
return cb_b_(remain, body, ec);
|
||||
return rd_.put(net::buffer(
|
||||
body.data(), body.size()), ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_finish_impl(
|
||||
error_code& ec) override
|
||||
{
|
||||
rd_.finish(ec);
|
||||
}
|
||||
};
|
||||
|
||||
/// An HTTP/1 parser for producing a request message.
|
||||
template<class Body, class Allocator = std::allocator<char>>
|
||||
using request_parser = parser<true, Body, Allocator>;
|
||||
|
||||
/// An HTTP/1 parser for producing a response message.
|
||||
template<class Body, class Allocator = std::allocator<char>>
|
||||
using response_parser = parser<false, Body, Allocator>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/parser.hpp>
|
||||
|
||||
#endif
|
||||
+882
@@ -0,0 +1,882 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_READ_HPP
|
||||
#define BOOST_BEAST_HTTP_READ_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/stream_traits.hpp>
|
||||
#include <boost/beast/http/basic_parser.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read part of a message from a stream using a parser.
|
||||
|
||||
This function is used to read part of a message from a stream into an
|
||||
instance of @ref basic_parser. The call will block until one of the
|
||||
following conditions is true:
|
||||
|
||||
@li A call to @ref basic_parser::put with a non-empty buffer sequence
|
||||
is successful.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`read_some` function. The implementation may read additional bytes from
|
||||
the stream that lie past the end of the message being read. These additional
|
||||
bytes are stored in the dynamic buffer, which must be preserved for
|
||||
subsequent reads.
|
||||
|
||||
If the end of file error is received while reading from the stream, then
|
||||
the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no bytes were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any bytes were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. The next attempt to read will return
|
||||
@ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read. The type must
|
||||
meet the <em>SyncReadStream</em> requirements.
|
||||
|
||||
@param buffer Storage for additional bytes read by the implementation from
|
||||
the stream. This is both an input and an output parameter; on entry, the
|
||||
parser will be presented with any remaining data in the dynamic buffer's
|
||||
readable bytes sequence first. The type must meet the <em>DynamicBuffer</em>
|
||||
requirements.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@return The number of bytes transferred from the stream.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@note The function returns the total number of bytes transferred from the
|
||||
stream. This may be zero for the case where there is sufficient pre-existing
|
||||
message data in the dynamic buffer.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser);
|
||||
|
||||
/** Read part of a message from a stream using a parser.
|
||||
|
||||
This function is used to read part of a message from a stream into an
|
||||
instance of @ref basic_parser. The call will block until one of the
|
||||
following conditions is true:
|
||||
|
||||
@li A call to @ref basic_parser::put with a non-empty buffer sequence
|
||||
is successful.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`read_some` function. The implementation may read additional bytes from
|
||||
the stream that lie past the end of the message being read. These additional
|
||||
bytes are stored in the dynamic buffer, which must be preserved for
|
||||
subsequent reads.
|
||||
|
||||
If the end of file error is received while reading from the stream, then
|
||||
the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no bytes were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any bytes were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. The next attempt to read will return
|
||||
@ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read. The type must
|
||||
support the <em>SyncReadStream</em> requirements.
|
||||
|
||||
@param buffer Storage for additional bytes read by the implementation from
|
||||
the stream. This is both an input and an output parameter; on entry, the
|
||||
parser will be presented with any remaining data in the dynamic buffer's
|
||||
readable bytes sequence first. The type must meet the <em>DynamicBuffer</em>
|
||||
requirements.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of bytes transferred from the stream.
|
||||
|
||||
@note The function returns the total number of bytes transferred from the
|
||||
stream. This may be zero for the case where there is sufficient pre-existing
|
||||
message data in the dynamic buffer.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Read part of a message asynchronously from a stream using a parser.
|
||||
|
||||
This function is used to asynchronously read part of a message from
|
||||
a stream into an instance of @ref basic_parser. The function call
|
||||
always returns immediately. The asynchronous operation will continue
|
||||
until one of the following conditions is true:
|
||||
|
||||
@li A call to @ref basic_parser::put with a non-empty buffer sequence
|
||||
is successful.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the
|
||||
next layer's `async_read_some` function, and is known as a <em>composed
|
||||
operation</em>. The program must ensure that the stream performs no other
|
||||
reads until this operation completes. The implementation may read additional
|
||||
bytes from the stream that lie past the end of the message being read.
|
||||
These additional bytes are stored in the dynamic buffer, which must be
|
||||
preserved for subsequent reads.
|
||||
|
||||
If the end of file error is received while reading from the stream, then
|
||||
the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no bytes were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any bytes were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. The next attempt to read will return
|
||||
@ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read. The type
|
||||
must meet the <em>AsyncReadStream</em> requirements.
|
||||
|
||||
@param buffer Storage for additional bytes read by the implementation from
|
||||
the stream. This is both an input and an output parameter; on entry, the
|
||||
parser will be presented with any remaining data in the dynamic buffer's
|
||||
readable bytes sequence first. The type must meet the <em>DynamicBuffer</em>
|
||||
requirements. The object must remain valid at least until the handler
|
||||
is called; ownership is not transferred.
|
||||
|
||||
@param parser The parser to use. The object must remain valid at least until
|
||||
the handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The completion handler to invoke when the operation
|
||||
completes. The implementation takes ownership of the handler by
|
||||
performing a decay-copy. The equivalent function signature of
|
||||
the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
error_code const& error, // result of operation
|
||||
std::size_t bytes_transferred // the total number of bytes transferred from the stream
|
||||
);
|
||||
@endcode
|
||||
If the handler has an associated immediate executor,
|
||||
an immediate completion will be dispatched to it.
|
||||
Otherwise, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@note The completion handler will receive as a parameter the total number
|
||||
of bytes transferred from the stream. This may be zero for the case where
|
||||
there is sufficient pre-existing message data in the dynamic buffer.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncReadStream>>>
|
||||
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
ReadHandler&& handler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncReadStream>>{});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read a complete message header from a stream using a parser.
|
||||
|
||||
This function is used to read a complete message header from a stream
|
||||
into an instance of @ref basic_parser. The call will block until one of the
|
||||
following conditions is true:
|
||||
|
||||
@li @ref basic_parser::is_header_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`read_some` function. The implementation may read additional bytes from
|
||||
the stream that lie past the end of the message being read. These additional
|
||||
bytes are stored in the dynamic buffer, which must be preserved for
|
||||
subsequent reads.
|
||||
|
||||
If the end of file error is received while reading from the stream, then
|
||||
the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no bytes were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any bytes were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. The next attempt to read will return
|
||||
@ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read. The type must
|
||||
meet the <em>SyncReadStream</em> requirements.
|
||||
|
||||
@param buffer Storage for additional bytes read by the implementation from
|
||||
the stream. This is both an input and an output parameter; on entry, the
|
||||
parser will be presented with any remaining data in the dynamic buffer's
|
||||
readable bytes sequence first. The type must meet the <em>DynamicBuffer</em>
|
||||
requirements.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@return The number of bytes transferred from the stream.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@note The function returns the total number of bytes transferred from the
|
||||
stream. This may be zero for the case where there is sufficient pre-existing
|
||||
message data in the dynamic buffer. The implementation will call
|
||||
@ref basic_parser::eager with the value `false` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser);
|
||||
|
||||
/** Read a complete message header from a stream using a parser.
|
||||
|
||||
This function is used to read a complete message header from a stream
|
||||
into an instance of @ref basic_parser. The call will block until one of the
|
||||
following conditions is true:
|
||||
|
||||
@li @ref basic_parser::is_header_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`read_some` function. The implementation may read additional bytes from
|
||||
the stream that lie past the end of the message being read. These additional
|
||||
bytes are stored in the dynamic buffer, which must be preserved for
|
||||
subsequent reads.
|
||||
|
||||
If the end of file error is received while reading from the stream, then
|
||||
the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no bytes were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any bytes were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. The next attempt to read will return
|
||||
@ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read. The type must
|
||||
meet the <em>SyncReadStream</em> requirements.
|
||||
|
||||
@param buffer Storage for additional bytes read by the implementation from
|
||||
the stream. This is both an input and an output parameter; on entry, the
|
||||
parser will be presented with any remaining data in the dynamic buffer's
|
||||
readable bytes sequence first. The type must meet the <em>DynamicBuffer</em>
|
||||
requirements.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of bytes transferred from the stream.
|
||||
|
||||
@note The function returns the total number of bytes transferred from the
|
||||
stream. This may be zero for the case where there is sufficient pre-existing
|
||||
message data in the dynamic buffer. The implementation will call
|
||||
@ref basic_parser::eager with the value `false` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Read a complete message header asynchronously from a stream using a parser.
|
||||
|
||||
This function is used to asynchronously read a complete message header from
|
||||
a stream into an instance of @ref basic_parser. The function call always
|
||||
returns immediately. The asynchronous operation will continue until one of
|
||||
the following conditions is true:
|
||||
|
||||
@li @ref basic_parser::is_header_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the
|
||||
next layer's `async_read_some` function, and is known as a <em>composed
|
||||
operation</em>. The program must ensure that the stream performs no other
|
||||
reads until this operation completes. The implementation may read additional
|
||||
bytes from the stream that lie past the end of the message being read.
|
||||
These additional bytes are stored in the dynamic buffer, which must be
|
||||
preserved for subsequent reads.
|
||||
|
||||
If the end of file error is received while reading from the stream, then
|
||||
the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no bytes were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any bytes were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. The next attempt to read will return
|
||||
@ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read. The type
|
||||
must meet the <em>AsyncReadStream</em> requirements.
|
||||
|
||||
@param buffer Storage for additional bytes read by the implementation from
|
||||
the stream. This is both an input and an output parameter; on entry, the
|
||||
parser will be presented with any remaining data in the dynamic buffer's
|
||||
readable bytes sequence first. The type must meet the <em>DynamicBuffer</em>
|
||||
requirements. The object must remain valid at least until the handler
|
||||
is called; ownership is not transferred.
|
||||
|
||||
@param parser The parser to use. The object must remain valid at least until
|
||||
the handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The completion handler to invoke when the operation
|
||||
completes. The implementation takes ownership of the handler by
|
||||
performing a decay-copy. The equivalent function signature of
|
||||
the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
error_code const& error, // result of operation
|
||||
std::size_t bytes_transferred // the total number of bytes transferred from the stream
|
||||
);
|
||||
@endcode
|
||||
If the handler has an associated immediate executor,
|
||||
an immediate completion will be dispatched to it.
|
||||
Otherwise, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@note The completion handler will receive as a parameter the total number
|
||||
of bytes transferred from the stream. This may be zero for the case where
|
||||
there is sufficient pre-existing message data in the dynamic buffer. The
|
||||
implementation will call @ref basic_parser::eager with the value `false`
|
||||
on the parser passed in.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncReadStream>>>
|
||||
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
|
||||
async_read_header(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
ReadHandler&& handler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncReadStream>>{});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read a complete message from a stream using a parser.
|
||||
|
||||
This function is used to read a complete message from a stream into an
|
||||
instance of @ref basic_parser. The call will block until one of the
|
||||
following conditions is true:
|
||||
|
||||
@li @ref basic_parser::is_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`read_some` function. The implementation may read additional bytes from
|
||||
the stream that lie past the end of the message being read. These additional
|
||||
bytes are stored in the dynamic buffer, which must be preserved for
|
||||
subsequent reads.
|
||||
|
||||
If the end of file error is received while reading from the stream, then
|
||||
the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no bytes were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any bytes were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. The next attempt to read will return
|
||||
@ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read. The type must
|
||||
meet the <em>SyncReadStream</em> requirements.
|
||||
|
||||
@param buffer Storage for additional bytes read by the implementation from
|
||||
the stream. This is both an input and an output parameter; on entry, the
|
||||
parser will be presented with any remaining data in the dynamic buffer's
|
||||
readable bytes sequence first. The type must meet the <em>DynamicBuffer</em>
|
||||
requirements.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@return The number of bytes transferred from the stream.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@note The function returns the total number of bytes transferred from the
|
||||
stream. This may be zero for the case where there is sufficient pre-existing
|
||||
message data in the dynamic buffer. The implementation will call
|
||||
@ref basic_parser::eager with the value `true` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser);
|
||||
|
||||
/** Read a complete message from a stream using a parser.
|
||||
|
||||
This function is used to read a complete message from a stream into an
|
||||
instance of @ref basic_parser. The call will block until one of the
|
||||
following conditions is true:
|
||||
|
||||
@li @ref basic_parser::is_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`read_some` function. The implementation may read additional bytes from
|
||||
the stream that lie past the end of the message being read. These additional
|
||||
bytes are stored in the dynamic buffer, which must be preserved for
|
||||
subsequent reads.
|
||||
|
||||
If the end of file error is received while reading from the stream, then
|
||||
the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no bytes were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any bytes were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. The next attempt to read will return
|
||||
@ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read. The type must
|
||||
meet the <em>SyncReadStream</em> requirements.
|
||||
|
||||
@param buffer Storage for additional bytes read by the implementation from
|
||||
the stream. This is both an input and an output parameter; on entry, the
|
||||
parser will be presented with any remaining data in the dynamic buffer's
|
||||
readable bytes sequence first. The type must meet the <em>DynamicBuffer</em>
|
||||
requirements.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of bytes transferred from the stream.
|
||||
|
||||
@note The function returns the total number of bytes transferred from the
|
||||
stream. This may be zero for the case where there is sufficient pre-existing
|
||||
message data in the dynamic buffer. The implementation will call
|
||||
@ref basic_parser::eager with the value `true` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest>
|
||||
std::size_t
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Read a complete message asynchronously from a stream using a parser.
|
||||
|
||||
This function is used to asynchronously read a complete message from a
|
||||
stream into an instance of @ref basic_parser. The function call always
|
||||
returns immediately. The asynchronous operation will continue until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li @ref basic_parser::is_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the
|
||||
next layer's `async_read_some` function, and is known as a <em>composed
|
||||
operation</em>. The program must ensure that the stream performs no other
|
||||
reads until this operation completes. The implementation may read additional
|
||||
bytes from the stream that lie past the end of the message being read.
|
||||
These additional bytes are stored in the dynamic buffer, which must be
|
||||
preserved for subsequent reads.
|
||||
|
||||
If the end of file error is received while reading from the stream, then
|
||||
the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no bytes were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any bytes were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. The next attempt to read will return
|
||||
@ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read. The type
|
||||
must meet the <em>AsyncReadStream</em> requirements.
|
||||
|
||||
@param buffer Storage for additional bytes read by the implementation from
|
||||
the stream. This is both an input and an output parameter; on entry, the
|
||||
parser will be presented with any remaining data in the dynamic buffer's
|
||||
readable bytes sequence first. The type must meet the <em>DynamicBuffer</em>
|
||||
requirements. The object must remain valid at least until the handler
|
||||
is called; ownership is not transferred.
|
||||
|
||||
@param parser The parser to use. The object must remain valid at least until
|
||||
the handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The completion handler to invoke when the operation
|
||||
completes. The implementation takes ownership of the handler by
|
||||
performing a decay-copy. The equivalent function signature of
|
||||
the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
error_code const& error, // result of operation
|
||||
std::size_t bytes_transferred // the total number of bytes transferred from the stream
|
||||
);
|
||||
@endcode
|
||||
If the handler has an associated immediate executor,
|
||||
an immediate completion will be dispatched to it.
|
||||
Otherwise, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@note The completion handler will receive as a parameter the total number
|
||||
of bytes transferred from the stream. This may be zero for the case where
|
||||
there is sufficient pre-existing message data in the dynamic buffer. The
|
||||
implementation will call @ref basic_parser::eager with the value `true`
|
||||
on the parser passed in.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncReadStream>>>
|
||||
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest>& parser,
|
||||
ReadHandler&& handler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncReadStream>>{});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read a complete message from a stream.
|
||||
|
||||
This function is used to read a complete message from a stream into an
|
||||
instance of @ref message. The call will block until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li The entire message is read in.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`read_some` function. The implementation may read additional bytes from
|
||||
the stream that lie past the end of the message being read. These additional
|
||||
bytes are stored in the dynamic buffer, which must be preserved for
|
||||
subsequent reads.
|
||||
|
||||
If the end of file error is received while reading from the stream, then
|
||||
the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no bytes were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any bytes were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. The next attempt to read will return
|
||||
@ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read. The type must
|
||||
meet the <em>SyncReadStream</em> requirements.
|
||||
|
||||
@param buffer Storage for additional bytes read by the implementation from
|
||||
the stream. This is both an input and an output parameter; on entry, the
|
||||
parser will be presented with any remaining data in the dynamic buffer's
|
||||
readable bytes sequence first. The type must meet the <em>DynamicBuffer</em>
|
||||
requirements.
|
||||
|
||||
@param msg The container in which to store the message contents. This
|
||||
message container should not have previous contents, otherwise the behavior
|
||||
is undefined. The type must be meet the <em>MoveAssignable</em> and
|
||||
<em>MoveConstructible</em> requirements.
|
||||
|
||||
@return The number of bytes transferred from the stream.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@note The function returns the total number of bytes transferred from the
|
||||
stream. This may be zero for the case where there is sufficient pre-existing
|
||||
message data in the dynamic buffer. The implementation will call
|
||||
@ref basic_parser::eager with the value `true` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
std::size_t
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg);
|
||||
|
||||
/** Read a complete message from a stream.
|
||||
|
||||
This function is used to read a complete message from a stream into an
|
||||
instance of @ref message. The call will block until one of the following
|
||||
conditions is true:
|
||||
|
||||
@li The entire message is read in.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`read_some` function. The implementation may read additional bytes from
|
||||
the stream that lie past the end of the message being read. These additional
|
||||
bytes are stored in the dynamic buffer, which must be preserved for
|
||||
subsequent reads.
|
||||
|
||||
If the end of file error is received while reading from the stream, then
|
||||
the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no bytes were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any bytes were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. The next attempt to read will return
|
||||
@ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read. The type must
|
||||
meet the <em>SyncReadStream</em> requirements.
|
||||
|
||||
@param buffer Storage for additional bytes read by the implementation from
|
||||
the stream. This is both an input and an output parameter; on entry, the
|
||||
parser will be presented with any remaining data in the dynamic buffer's
|
||||
readable bytes sequence first. The type must meet the <em>DynamicBuffer</em>
|
||||
requirements.
|
||||
|
||||
@param msg The container in which to store the message contents. This
|
||||
message container should not have previous contents, otherwise the behavior
|
||||
is undefined. The type must be meet the <em>MoveAssignable</em> and
|
||||
<em>MoveConstructible</em> requirements.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of bytes transferred from the stream.
|
||||
|
||||
@note The function returns the total number of bytes transferred from the
|
||||
stream. This may be zero for the case where there is sufficient pre-existing
|
||||
message data in the dynamic buffer. The implementation will call
|
||||
@ref basic_parser::eager with the value `true` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator>
|
||||
std::size_t
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Read a complete message asynchronously from a stream.
|
||||
|
||||
This function is used to asynchronously read a complete message from a
|
||||
stream into an instance of @ref message. The function call always returns
|
||||
immediately. The asynchronous operation will continue until one of the
|
||||
following conditions is true:
|
||||
|
||||
@li The entire message is read in.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the
|
||||
next layer's `async_read_some` function, and is known as a <em>composed
|
||||
operation</em>. The program must ensure that the stream performs no other
|
||||
reads until this operation completes. The implementation may read additional
|
||||
bytes from the stream that lie past the end of the message being read.
|
||||
These additional bytes are stored in the dynamic buffer, which must be
|
||||
preserved for subsequent reads.
|
||||
|
||||
If the end of file error is received while reading from the stream, then
|
||||
the error returned from this function will be:
|
||||
|
||||
@li @ref error::end_of_stream if no bytes were parsed, or
|
||||
|
||||
@li @ref error::partial_message if any bytes were parsed but the
|
||||
message was incomplete, otherwise:
|
||||
|
||||
@li A successful result. The next attempt to read will return
|
||||
@ref error::end_of_stream
|
||||
|
||||
@param stream The stream from which the data is to be read. The type
|
||||
must meet the <em>AsyncReadStream</em> requirements.
|
||||
|
||||
@param buffer Storage for additional bytes read by the implementation from
|
||||
the stream. This is both an input and an output parameter; on entry, the
|
||||
parser will be presented with any remaining data in the dynamic buffer's
|
||||
readable bytes sequence first. The type must meet the <em>DynamicBuffer</em>
|
||||
requirements. The object must remain valid at least until the handler
|
||||
is called; ownership is not transferred.
|
||||
|
||||
@param msg The container in which to store the message contents. This
|
||||
message container should not have previous contents, otherwise the behavior
|
||||
is undefined. The type must be meet the <em>MoveAssignable</em> and
|
||||
<em>MoveConstructible</em> requirements. The object must remain valid
|
||||
at least until the handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The completion handler to invoke when the operation
|
||||
completes. The implementation takes ownership of the handler by
|
||||
performing a decay-copy. The equivalent function signature of
|
||||
the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
error_code const& error, // result of operation
|
||||
std::size_t bytes_transferred // the total number of bytes transferred from the stream
|
||||
);
|
||||
@endcode
|
||||
If the handler has an associated immediate executor,
|
||||
an immediate completion will be dispatched to it.
|
||||
Otherwise, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@note The completion handler will receive as a parameter the total number
|
||||
of bytes transferred from the stream. This may be zero for the case where
|
||||
there is sufficient pre-existing message data in the dynamic buffer. The
|
||||
implementation will call @ref basic_parser::eager with the value `true`
|
||||
on the parser passed in.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Allocator,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncReadStream>>>
|
||||
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, basic_fields<Allocator>>& msg,
|
||||
ReadHandler&& handler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncReadStream>>{});
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/read.hpp>
|
||||
|
||||
#endif
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_RFC7230_HPP
|
||||
#define BOOST_BEAST_HTTP_RFC7230_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/http/detail/rfc7230.hpp>
|
||||
#include <boost/beast/http/detail/basic_parsed_list.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A list of parameters in an HTTP extension field value.
|
||||
|
||||
This container allows iteration of the parameter list in an HTTP
|
||||
extension. The parameter list is a series of name/value pairs
|
||||
with each pair starting with a semicolon. The value is optional.
|
||||
|
||||
If a parsing error is encountered while iterating the string,
|
||||
the behavior of the container will be as if a string containing
|
||||
only characters up to but excluding the first invalid character
|
||||
was used to construct the list.
|
||||
|
||||
@par BNF
|
||||
@code
|
||||
param-list = *( OWS ";" OWS param )
|
||||
param = token OWS [ "=" OWS ( token / quoted-string ) ]
|
||||
@endcode
|
||||
|
||||
To use this class, construct with the string to be parsed and
|
||||
then use @ref begin and @ref end, or range-for to iterate each
|
||||
item:
|
||||
|
||||
@par Example
|
||||
@code
|
||||
for(auto const& param : param_list{";level=9;no_context_takeover;bits=15"})
|
||||
{
|
||||
std::cout << ";" << param.first;
|
||||
if(! param.second.empty())
|
||||
std::cout << "=" << param.second;
|
||||
std::cout << "\n";
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
class param_list
|
||||
{
|
||||
string_view s_;
|
||||
|
||||
public:
|
||||
/** The type of each element in the list.
|
||||
|
||||
The first string in the pair is the name of the parameter,
|
||||
and the second string in the pair is its value (which may
|
||||
be empty).
|
||||
*/
|
||||
using value_type =
|
||||
std::pair<string_view, string_view>;
|
||||
|
||||
/// A constant iterator to the list
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = __implementation_defined__;
|
||||
#else
|
||||
class const_iterator;
|
||||
#endif
|
||||
|
||||
/// Default constructor.
|
||||
param_list() = default;
|
||||
|
||||
/** Construct a list.
|
||||
|
||||
@param s A string containing the list contents. The string
|
||||
must remain valid for the lifetime of the container.
|
||||
*/
|
||||
explicit
|
||||
param_list(string_view s)
|
||||
: s_(s)
|
||||
{
|
||||
}
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator begin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator end() const;
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator cbegin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator cend() const;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A list of extensions in a comma separated HTTP field value.
|
||||
|
||||
This container allows iteration of the extensions in an HTTP
|
||||
field value. The extension list is a comma separated list of
|
||||
token parameter list pairs.
|
||||
|
||||
If a parsing error is encountered while iterating the string,
|
||||
the behavior of the container will be as if a string containing
|
||||
only characters up to but excluding the first invalid character
|
||||
was used to construct the list.
|
||||
|
||||
@par BNF
|
||||
@code
|
||||
ext-list = *( "," OWS ) ext *( OWS "," [ OWS ext ] )
|
||||
ext = token param-list
|
||||
param-list = *( OWS ";" OWS param )
|
||||
param = token OWS [ "=" OWS ( token / quoted-string ) ]
|
||||
@endcode
|
||||
|
||||
To use this class, construct with the string to be parsed and
|
||||
then use @ref begin and @ref end, or range-for to iterate each
|
||||
item:
|
||||
|
||||
@par Example
|
||||
@code
|
||||
for(auto const& ext : ext_list{"none, 7z;level=9, zip;no_context_takeover;bits=15"})
|
||||
{
|
||||
std::cout << ext.first << "\n";
|
||||
for(auto const& param : ext.second)
|
||||
{
|
||||
std::cout << ";" << param.first;
|
||||
if(! param.second.empty())
|
||||
std::cout << "=" << param.second;
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
class ext_list
|
||||
{
|
||||
using iter_type = string_view::const_iterator;
|
||||
|
||||
string_view s_;
|
||||
|
||||
public:
|
||||
/** The type of each element in the list.
|
||||
|
||||
The first element of the pair is the extension token, and the
|
||||
second element of the pair is an iterable container holding the
|
||||
extension's name/value parameters.
|
||||
*/
|
||||
using value_type = std::pair<string_view, param_list>;
|
||||
|
||||
/// A constant iterator to the list
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = __implementation_defined__;
|
||||
#else
|
||||
class const_iterator;
|
||||
#endif
|
||||
|
||||
/** Construct a list.
|
||||
|
||||
@param s A string containing the list contents. The string
|
||||
must remain valid for the lifetime of the container.
|
||||
*/
|
||||
explicit
|
||||
ext_list(string_view s)
|
||||
: s_(s)
|
||||
{
|
||||
}
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator begin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator end() const;
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator cbegin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator cend() const;
|
||||
|
||||
/** Find a token in the list.
|
||||
|
||||
@param s The token to find. A case-insensitive comparison is used.
|
||||
|
||||
@return An iterator to the matching token, or `end()` if no
|
||||
token exists.
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
const_iterator
|
||||
find(string_view const& s);
|
||||
|
||||
/** Return `true` if a token is present in the list.
|
||||
|
||||
@param s The token to find. A case-insensitive comparison is used.
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
bool
|
||||
exists(string_view const& s);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** A list of tokens in a comma separated HTTP field value.
|
||||
|
||||
This container allows iteration of a list of items in a
|
||||
header field value. The input is a comma separated list of
|
||||
tokens.
|
||||
|
||||
If a parsing error is encountered while iterating the string,
|
||||
the behavior of the container will be as if a string containing
|
||||
only characters up to but excluding the first invalid character
|
||||
was used to construct the list.
|
||||
|
||||
@par BNF
|
||||
@code
|
||||
token-list = *( "," OWS ) token *( OWS "," [ OWS token ] )
|
||||
@endcode
|
||||
|
||||
To use this class, construct with the string to be parsed and
|
||||
then use @ref begin and @ref end, or range-for to iterate each
|
||||
item:
|
||||
|
||||
@par Example
|
||||
@code
|
||||
for(auto const& token : token_list{"apple, pear, banana"})
|
||||
std::cout << token << "\n";
|
||||
@endcode
|
||||
*/
|
||||
class token_list
|
||||
{
|
||||
using iter_type = string_view::const_iterator;
|
||||
|
||||
string_view s_;
|
||||
|
||||
public:
|
||||
/// The type of each element in the token list.
|
||||
using value_type = string_view;
|
||||
|
||||
/// A constant iterator to the list
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using const_iterator = __implementation_defined__;
|
||||
#else
|
||||
class const_iterator;
|
||||
#endif
|
||||
|
||||
/** Construct a list.
|
||||
|
||||
@param s A string containing the list contents. The string
|
||||
must remain valid for the lifetime of the container.
|
||||
*/
|
||||
explicit
|
||||
token_list(string_view s)
|
||||
: s_(s)
|
||||
{
|
||||
}
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator begin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator end() const;
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator cbegin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator cend() const;
|
||||
|
||||
/** Return `true` if a token is present in the list.
|
||||
|
||||
@param s The token to find. A case-insensitive comparison is used.
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
bool
|
||||
exists(string_view const& s);
|
||||
};
|
||||
|
||||
/** A list of tokens in a comma separated HTTP field value.
|
||||
|
||||
This container allows iteration of a list of items in a
|
||||
header field value. The input is a comma separated list of
|
||||
tokens.
|
||||
|
||||
If a parsing error is encountered while iterating the string,
|
||||
the behavior of the container will be as if a string containing
|
||||
only characters up to but excluding the first invalid character
|
||||
was used to construct the list.
|
||||
|
||||
@par BNF
|
||||
@code
|
||||
token-list = *( "," OWS ) token *( OWS "," [ OWS token ] )
|
||||
@endcode
|
||||
|
||||
To use this class, construct with the string to be parsed and
|
||||
then use `begin` and `end`, or range-for to iterate each item:
|
||||
|
||||
@par Example
|
||||
@code
|
||||
for(auto const& token : token_list{"apple, pear, banana"})
|
||||
std::cout << token << "\n";
|
||||
@endcode
|
||||
*/
|
||||
using opt_token_list =
|
||||
detail::basic_parsed_list<
|
||||
detail::opt_token_list_policy>;
|
||||
|
||||
/** Returns `true` if a parsed list is parsed without errors.
|
||||
|
||||
This function iterates a single pass through a parsed list
|
||||
and returns `true` if there were no parsing errors, else
|
||||
returns `false`.
|
||||
*/
|
||||
template<class Policy>
|
||||
bool
|
||||
validate_list(detail::basic_parsed_list<
|
||||
Policy> const& list);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/rfc7230.hpp>
|
||||
|
||||
#endif
|
||||
+370
@@ -0,0 +1,370 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_SERIALIZER_HPP
|
||||
#define BOOST_BEAST_HTTP_SERIALIZER_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/buffers_cat.hpp>
|
||||
#include <boost/beast/core/buffers_prefix.hpp>
|
||||
#include <boost/beast/core/buffers_suffix.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/core/detail/variant.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/chunk_encode.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Provides buffer oriented HTTP message serialization functionality.
|
||||
|
||||
An object of this type is used to serialize a complete
|
||||
HTTP message into a sequence of octets. To use this class,
|
||||
construct an instance with the message to be serialized.
|
||||
The implementation will automatically perform chunk encoding
|
||||
if the contents of the message indicate that chunk encoding
|
||||
is required.
|
||||
|
||||
Chunked output produced by the serializer never contains chunk
|
||||
extensions or trailers, and the location of chunk boundaries
|
||||
is not specified. If callers require chunk extensions, trailers,
|
||||
or control over the exact contents of each chunk they should
|
||||
use the serializer to write just the message header, and then
|
||||
assume control over serializing the chunked payload by using
|
||||
the chunk buffer sequence types @ref chunk_body, @ref chunk_crlf,
|
||||
@ref chunk_header, and @ref chunk_last.
|
||||
|
||||
@tparam isRequest `true` if the message is a request.
|
||||
|
||||
@tparam Body The body type of the message.
|
||||
|
||||
@tparam Fields The type of fields in the message.
|
||||
*/
|
||||
template<
|
||||
bool isRequest,
|
||||
class Body,
|
||||
class Fields = fields>
|
||||
class serializer
|
||||
{
|
||||
public:
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter type requirements not met");
|
||||
|
||||
/** The type of message this serializer uses
|
||||
|
||||
This may be const or non-const depending on the
|
||||
implementation of the corresponding <em>BodyWriter</em>.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using value_type = __implementation_defined__;
|
||||
#else
|
||||
using value_type = typename std::conditional<
|
||||
std::is_constructible<typename Body::writer,
|
||||
header<isRequest, Fields>&,
|
||||
typename Body::value_type&>::value &&
|
||||
! std::is_constructible<typename Body::writer,
|
||||
header<isRequest, Fields> const&,
|
||||
typename Body::value_type const&>::value,
|
||||
message<isRequest, Body, Fields>,
|
||||
message<isRequest, Body, Fields> const>::type;
|
||||
#endif
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
do_construct = 0,
|
||||
|
||||
do_init = 10,
|
||||
do_header_only = 20,
|
||||
do_header = 30,
|
||||
do_body = 40,
|
||||
|
||||
do_init_c = 50,
|
||||
do_header_only_c = 60,
|
||||
do_header_c = 70,
|
||||
do_body_c = 80,
|
||||
do_final_c = 90,
|
||||
#ifndef BOOST_BEAST_NO_BIG_VARIANTS
|
||||
do_body_final_c = 100,
|
||||
do_all_c = 110,
|
||||
#endif
|
||||
|
||||
do_complete = 120
|
||||
};
|
||||
|
||||
void fwrinit(std::true_type);
|
||||
void fwrinit(std::false_type);
|
||||
|
||||
template<std::size_t, class Visit>
|
||||
void
|
||||
do_visit(error_code& ec, Visit& visit);
|
||||
|
||||
using writer = typename Body::writer;
|
||||
|
||||
using cb1_t = buffers_suffix<typename
|
||||
Fields::writer::const_buffers_type>; // header
|
||||
using pcb1_t = buffers_prefix_view<cb1_t const&>;
|
||||
|
||||
using cb2_t = buffers_suffix<buffers_cat_view<
|
||||
typename Fields::writer::const_buffers_type,// header
|
||||
typename writer::const_buffers_type>>; // body
|
||||
using pcb2_t = buffers_prefix_view<cb2_t const&>;
|
||||
|
||||
using cb3_t = buffers_suffix<
|
||||
typename writer::const_buffers_type>; // body
|
||||
using pcb3_t = buffers_prefix_view<cb3_t const&>;
|
||||
|
||||
using cb4_t = buffers_suffix<buffers_cat_view<
|
||||
typename Fields::writer::const_buffers_type,// header
|
||||
detail::chunk_size, // chunk-size
|
||||
net::const_buffer, // chunk-ext
|
||||
chunk_crlf, // crlf
|
||||
typename writer::const_buffers_type, // body
|
||||
chunk_crlf>>; // crlf
|
||||
using pcb4_t = buffers_prefix_view<cb4_t const&>;
|
||||
|
||||
using cb5_t = buffers_suffix<buffers_cat_view<
|
||||
detail::chunk_size, // chunk-header
|
||||
net::const_buffer, // chunk-ext
|
||||
chunk_crlf, // crlf
|
||||
typename writer::const_buffers_type, // body
|
||||
chunk_crlf>>; // crlf
|
||||
using pcb5_t = buffers_prefix_view<cb5_t const&>;
|
||||
|
||||
using cb6_t = buffers_suffix<buffers_cat_view<
|
||||
detail::chunk_size, // chunk-header
|
||||
net::const_buffer, // chunk-size
|
||||
chunk_crlf, // crlf
|
||||
typename writer::const_buffers_type, // body
|
||||
chunk_crlf, // crlf
|
||||
net::const_buffer, // chunk-final
|
||||
net::const_buffer, // trailers
|
||||
chunk_crlf>>; // crlf
|
||||
using pcb6_t = buffers_prefix_view<cb6_t const&>;
|
||||
|
||||
using cb7_t = buffers_suffix<buffers_cat_view<
|
||||
typename Fields::writer::const_buffers_type,// header
|
||||
detail::chunk_size, // chunk-size
|
||||
net::const_buffer, // chunk-ext
|
||||
chunk_crlf, // crlf
|
||||
typename writer::const_buffers_type, // body
|
||||
chunk_crlf, // crlf
|
||||
net::const_buffer, // chunk-final
|
||||
net::const_buffer, // trailers
|
||||
chunk_crlf>>; // crlf
|
||||
using pcb7_t = buffers_prefix_view<cb7_t const&>;
|
||||
|
||||
using cb8_t = buffers_suffix<buffers_cat_view<
|
||||
net::const_buffer, // chunk-final
|
||||
net::const_buffer, // trailers
|
||||
chunk_crlf>>; // crlf
|
||||
using pcb8_t = buffers_prefix_view<cb8_t const&>;
|
||||
|
||||
value_type& m_;
|
||||
writer wr_;
|
||||
boost::optional<typename Fields::writer> fwr_;
|
||||
beast::detail::variant<
|
||||
cb1_t, cb2_t, cb3_t, cb4_t,
|
||||
cb5_t ,cb6_t, cb7_t, cb8_t> v_;
|
||||
beast::detail::variant<
|
||||
pcb1_t, pcb2_t, pcb3_t, pcb4_t,
|
||||
pcb5_t ,pcb6_t, pcb7_t, pcb8_t> pv_;
|
||||
std::size_t limit_ =
|
||||
(std::numeric_limits<std::size_t>::max)();
|
||||
int s_ = do_construct;
|
||||
bool split_ = false;
|
||||
bool header_done_ = false;
|
||||
bool more_ = false;
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
serializer(serializer&&) = default;
|
||||
|
||||
/// Constructor
|
||||
serializer(serializer const&) = default;
|
||||
|
||||
/// Assignment
|
||||
serializer& operator=(serializer const&) = delete;
|
||||
|
||||
/** Constructor
|
||||
|
||||
The implementation guarantees that the message passed on
|
||||
construction will not be accessed until the first call to
|
||||
@ref next. This allows the message to be lazily created.
|
||||
For example, if the header is filled in before serialization.
|
||||
|
||||
@param msg A reference to the message to serialize, which must
|
||||
remain valid for the lifetime of the serializer. Depending on
|
||||
the type of Body used, this may or may not be a `const` reference.
|
||||
|
||||
@note This function participates in overload resolution only if
|
||||
Body::writer is constructible from a `const` message reference.
|
||||
*/
|
||||
explicit
|
||||
serializer(value_type& msg);
|
||||
|
||||
/// Returns the message being serialized
|
||||
value_type&
|
||||
get()
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/// Returns the serialized buffer size limit
|
||||
std::size_t
|
||||
limit()
|
||||
{
|
||||
return limit_;
|
||||
}
|
||||
|
||||
/** Set the serialized buffer size limit
|
||||
|
||||
This function adjusts the limit on the maximum size of the
|
||||
buffers passed to the visitor. The new size limit takes effect
|
||||
in the following call to @ref next.
|
||||
|
||||
The default is no buffer size limit.
|
||||
|
||||
@param limit The new buffer size limit. If this number
|
||||
is zero, the size limit is removed.
|
||||
*/
|
||||
void
|
||||
limit(std::size_t limit)
|
||||
{
|
||||
limit_ = limit > 0 ? limit :
|
||||
(std::numeric_limits<std::size_t>::max)();
|
||||
}
|
||||
|
||||
/** Returns `true` if we will pause after writing the complete header.
|
||||
*/
|
||||
bool
|
||||
split()
|
||||
{
|
||||
return split_;
|
||||
}
|
||||
|
||||
/** Set whether the header and body are written separately.
|
||||
|
||||
When the split feature is enabled, the implementation will
|
||||
write only the octets corresponding to the serialized header
|
||||
first. If the header has already been written, this function
|
||||
will have no effect on output.
|
||||
*/
|
||||
void
|
||||
split(bool v)
|
||||
{
|
||||
split_ = v;
|
||||
}
|
||||
|
||||
/** Return `true` if serialization of the header is complete.
|
||||
|
||||
This function indicates whether or not all buffers containing
|
||||
serialized header octets have been retrieved.
|
||||
*/
|
||||
bool
|
||||
is_header_done()
|
||||
{
|
||||
return header_done_;
|
||||
}
|
||||
|
||||
/** Return `true` if serialization is complete.
|
||||
|
||||
The operation is complete when all octets corresponding
|
||||
to the serialized representation of the message have been
|
||||
successfully retrieved.
|
||||
*/
|
||||
bool
|
||||
is_done() const
|
||||
{
|
||||
return s_ == do_complete;
|
||||
}
|
||||
|
||||
/** Returns the next set of buffers in the serialization.
|
||||
|
||||
This function will attempt to call the `visit` function
|
||||
object with a <em>ConstBufferSequence</em> of unspecified type
|
||||
representing the next set of buffers in the serialization
|
||||
of the message represented by this object.
|
||||
|
||||
If there are no more buffers in the serialization, the
|
||||
visit function will not be called. In this case, no error
|
||||
will be indicated, and the function @ref is_done will
|
||||
return `true`.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@param visit The function to call. The equivalent function
|
||||
signature of this object must be:
|
||||
@code
|
||||
template<class ConstBufferSequence>
|
||||
void visit(error_code&, ConstBufferSequence const&);
|
||||
@endcode
|
||||
The function is not copied, if no error occurs it will be
|
||||
invoked before the call to @ref next returns.
|
||||
|
||||
*/
|
||||
template<class Visit>
|
||||
void
|
||||
next(error_code& ec, Visit&& visit);
|
||||
|
||||
/** Consume buffer octets in the serialization.
|
||||
|
||||
This function should be called after one or more octets
|
||||
contained in the buffers provided in the prior call
|
||||
to @ref next have been used.
|
||||
|
||||
After a call to @ref consume, callers should check the
|
||||
return value of @ref is_done to determine if the entire
|
||||
message has been serialized.
|
||||
|
||||
@param n The number of octets to consume. This number must
|
||||
be greater than zero and no greater than the number of
|
||||
octets in the buffers provided in the prior call to @ref next.
|
||||
*/
|
||||
void
|
||||
consume(std::size_t n);
|
||||
|
||||
/** Provides low-level access to the associated <em>BodyWriter</em>
|
||||
|
||||
This function provides access to the instance of the writer
|
||||
associated with the body and created by the serializer
|
||||
upon construction. The behavior of accessing this object
|
||||
is defined by the specification of the particular writer
|
||||
and its associated body.
|
||||
|
||||
@return A reference to the writer.
|
||||
*/
|
||||
writer&
|
||||
writer_impl()
|
||||
{
|
||||
return wr_;
|
||||
}
|
||||
};
|
||||
|
||||
/// A serializer for HTTP/1 requests
|
||||
template<class Body, class Fields = fields>
|
||||
using request_serializer = serializer<true, Body, Fields>;
|
||||
|
||||
/// A serializer for HTTP/1 responses
|
||||
template<class Body, class Fields = fields>
|
||||
using response_serializer = serializer<false, Body, Fields>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/serializer.hpp>
|
||||
|
||||
#endif
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_SPAN_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_SPAN_BODY_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#include <boost/beast/core/span.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A <em>Body</em> using @ref span
|
||||
|
||||
This body uses @ref span as a memory-based container for
|
||||
holding message payloads. The container represents a
|
||||
non-owning reference to a contiguous area of memory.
|
||||
Messages using this body type may be serialized and
|
||||
parsed.
|
||||
|
||||
Unlike @ref buffer_body, only one buffer may be provided
|
||||
during a parse or serialize operation.
|
||||
*/
|
||||
template<class T>
|
||||
struct span_body
|
||||
{
|
||||
private:
|
||||
static_assert(
|
||||
std::is_trivial<T>::value &&
|
||||
std::is_standard_layout<T>::value,
|
||||
"POD requirements not met");
|
||||
|
||||
public:
|
||||
/** The type of container used for the body
|
||||
|
||||
This determines the type of @ref message::body
|
||||
when this body type is used with a message container.
|
||||
*/
|
||||
using value_type = span<T>;
|
||||
|
||||
/** Returns the payload size of the body
|
||||
|
||||
When this body is used with @ref message::prepare_payload,
|
||||
the Content-Length will be set to the payload size, and
|
||||
any chunked Transfer-Encoding will be removed.
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& body)
|
||||
{
|
||||
return body.size();
|
||||
}
|
||||
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of <em>BodyReader</em>.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using reader = __implementation_defined__;
|
||||
#else
|
||||
class reader
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(header<isRequest, Fields>&, value_type& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> const& length, error_code& ec)
|
||||
{
|
||||
if(length && *length > body_.size())
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::buffer_overflow);
|
||||
return;
|
||||
}
|
||||
ec = {};
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
auto const n = buffer_bytes(buffers);
|
||||
auto const len = body_.size();
|
||||
if(n > len)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::buffer_overflow);
|
||||
return 0;
|
||||
}
|
||||
ec = {};
|
||||
net::buffer_copy(net::buffer(
|
||||
body_.data(), n), buffers);
|
||||
body_ = value_type{
|
||||
body_.data() + n, body_.size() - n};
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of <em>BodyWriter</em>.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using writer = __implementation_defined__;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type const& body_;
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
net::const_buffer;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(header<isRequest, Fields> const&, value_type const& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
return {{
|
||||
{ body_.data(),
|
||||
body_.size() * sizeof(typename
|
||||
value_type::value_type)},
|
||||
false}};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_STATUS_HPP
|
||||
#define BOOST_BEAST_HTTP_STATUS_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
enum class status : unsigned
|
||||
{
|
||||
/** An unknown status-code.
|
||||
|
||||
This value indicates that the value for the status code
|
||||
is not in the list of commonly recognized status codes.
|
||||
Callers interested in the exactly value should use the
|
||||
interface which provides the raw integer.
|
||||
*/
|
||||
unknown = 0,
|
||||
|
||||
continue_ = 100,
|
||||
|
||||
/** Switching Protocols
|
||||
|
||||
This status indicates that a request to switch to a new
|
||||
protocol was accepted and applied by the server. A successful
|
||||
response to a WebSocket Upgrade HTTP request will have this
|
||||
code.
|
||||
*/
|
||||
switching_protocols = 101,
|
||||
|
||||
processing = 102,
|
||||
|
||||
ok = 200,
|
||||
created = 201,
|
||||
accepted = 202,
|
||||
non_authoritative_information = 203,
|
||||
no_content = 204,
|
||||
reset_content = 205,
|
||||
partial_content = 206,
|
||||
multi_status = 207,
|
||||
already_reported = 208,
|
||||
im_used = 226,
|
||||
|
||||
multiple_choices = 300,
|
||||
moved_permanently = 301,
|
||||
found = 302,
|
||||
see_other = 303,
|
||||
not_modified = 304,
|
||||
use_proxy = 305,
|
||||
temporary_redirect = 307,
|
||||
permanent_redirect = 308,
|
||||
|
||||
bad_request = 400,
|
||||
unauthorized = 401,
|
||||
payment_required = 402,
|
||||
forbidden = 403,
|
||||
not_found = 404,
|
||||
method_not_allowed = 405,
|
||||
not_acceptable = 406,
|
||||
proxy_authentication_required = 407,
|
||||
request_timeout = 408,
|
||||
conflict = 409,
|
||||
gone = 410,
|
||||
length_required = 411,
|
||||
precondition_failed = 412,
|
||||
payload_too_large = 413,
|
||||
uri_too_long = 414,
|
||||
unsupported_media_type = 415,
|
||||
range_not_satisfiable = 416,
|
||||
expectation_failed = 417,
|
||||
misdirected_request = 421,
|
||||
unprocessable_entity = 422,
|
||||
locked = 423,
|
||||
failed_dependency = 424,
|
||||
upgrade_required = 426,
|
||||
precondition_required = 428,
|
||||
too_many_requests = 429,
|
||||
request_header_fields_too_large = 431,
|
||||
connection_closed_without_response = 444,
|
||||
unavailable_for_legal_reasons = 451,
|
||||
client_closed_request = 499,
|
||||
|
||||
internal_server_error = 500,
|
||||
not_implemented = 501,
|
||||
bad_gateway = 502,
|
||||
service_unavailable = 503,
|
||||
gateway_timeout = 504,
|
||||
http_version_not_supported = 505,
|
||||
variant_also_negotiates = 506,
|
||||
insufficient_storage = 507,
|
||||
loop_detected = 508,
|
||||
not_extended = 510,
|
||||
network_authentication_required = 511,
|
||||
network_connect_timeout_error = 599
|
||||
};
|
||||
|
||||
/** Represents the class of a status-code.
|
||||
*/
|
||||
enum class status_class : unsigned
|
||||
{
|
||||
/// Unknown status-class
|
||||
unknown = 0,
|
||||
|
||||
/// The request was received, continuing processing.
|
||||
informational = 1,
|
||||
|
||||
/// The request was successfully received, understood, and accepted.
|
||||
successful = 2,
|
||||
|
||||
/// Further action needs to be taken in order to complete the request.
|
||||
redirection = 3,
|
||||
|
||||
/// The request contains bad syntax or cannot be fulfilled.
|
||||
client_error = 4,
|
||||
|
||||
/// The server failed to fulfill an apparently valid request.
|
||||
server_error = 5,
|
||||
};
|
||||
|
||||
/** Converts the integer to a known status-code.
|
||||
|
||||
If the integer does not match a known status code,
|
||||
@ref status::unknown is returned.
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
status
|
||||
int_to_status(unsigned v);
|
||||
|
||||
/** Convert an integer to a status_class.
|
||||
|
||||
@param v The integer representing a status code.
|
||||
|
||||
@return The status class. If the integer does not match
|
||||
a known status class, @ref status_class::unknown is returned.
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
status_class
|
||||
to_status_class(unsigned v);
|
||||
|
||||
/** Convert a status_code to a status_class.
|
||||
|
||||
@param v The status code to convert.
|
||||
|
||||
@return The status class.
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
status_class
|
||||
to_status_class(status v);
|
||||
|
||||
/** Returns the obsolete reason-phrase text for a status code.
|
||||
|
||||
@param v The status code to use.
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
string_view
|
||||
obsolete_reason(status v);
|
||||
|
||||
/// Outputs the standard reason phrase of a status code to a stream.
|
||||
BOOST_BEAST_DECL
|
||||
std::ostream&
|
||||
operator<<(std::ostream&, status);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#ifdef BOOST_BEAST_HEADER_ONLY
|
||||
#include <boost/beast/http/impl/status.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_STRING_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_STRING_BODY_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/core/buffers_range.hpp>
|
||||
#include <boost/beast/core/detail/clamp.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A <em>Body</em> using `std::basic_string`
|
||||
|
||||
This body uses `std::basic_string` as a memory-based container
|
||||
for holding message payloads. Messages using this body type
|
||||
may be serialized and parsed.
|
||||
*/
|
||||
template<
|
||||
class CharT,
|
||||
class Traits = std::char_traits<CharT>,
|
||||
class Allocator = std::allocator<CharT>>
|
||||
struct basic_string_body
|
||||
{
|
||||
private:
|
||||
static_assert(
|
||||
std::is_integral<CharT>::value &&
|
||||
sizeof(CharT) == 1,
|
||||
"CharT requirements not met");
|
||||
|
||||
public:
|
||||
/** The type of container used for the body
|
||||
|
||||
This determines the type of @ref message::body
|
||||
when this body type is used with a message container.
|
||||
*/
|
||||
using value_type =
|
||||
std::basic_string<CharT, Traits, Allocator>;
|
||||
|
||||
/** Returns the payload size of the body
|
||||
|
||||
When this body is used with @ref message::prepare_payload,
|
||||
the Content-Length will be set to the payload size, and
|
||||
any chunked Transfer-Encoding will be removed.
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& body)
|
||||
{
|
||||
return body.size();
|
||||
}
|
||||
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of <em>BodyReader</em>.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using reader = __implementation_defined__;
|
||||
#else
|
||||
class reader
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(header<isRequest, Fields>&, value_type& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> const& length, error_code& ec)
|
||||
{
|
||||
if(length)
|
||||
{
|
||||
if(*length > body_.max_size())
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::buffer_overflow);
|
||||
return;
|
||||
}
|
||||
body_.reserve(beast::detail::clamp(*length));
|
||||
}
|
||||
ec = {};
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
auto const extra = buffer_bytes(buffers);
|
||||
auto const size = body_.size();
|
||||
if (extra > body_.max_size() - size)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::buffer_overflow);
|
||||
return 0;
|
||||
}
|
||||
|
||||
body_.resize(size + extra);
|
||||
ec = {};
|
||||
CharT* dest = &body_[size];
|
||||
for(auto b : beast::buffers_range_ref(buffers))
|
||||
{
|
||||
Traits::copy(dest, static_cast<
|
||||
CharT const*>(b.data()), b.size());
|
||||
dest += b.size();
|
||||
}
|
||||
return extra;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of <em>BodyWriter</em>.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using writer = __implementation_defined__;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type const& body_;
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
net::const_buffer;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(header<isRequest, Fields> const&, value_type const& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
return {{const_buffers_type{
|
||||
body_.data(), body_.size()}, false}};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
/// A <em>Body</em> using `std::string`
|
||||
using string_body = basic_string_body<char>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_TYPE_TRAITS_HPP
|
||||
#define BOOST_BEAST_HTTP_TYPE_TRAITS_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <boost/beast/http/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool, class, class>
|
||||
class message;
|
||||
|
||||
/** Determine if a type meets the <em>Body</em> named requirements.
|
||||
|
||||
This alias template is `std::true_type` if `T` meets
|
||||
the requirements, otherwise it is `std::false_type`.
|
||||
|
||||
@tparam T The type to test.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void check_body(message<isRequest, Body, Fields> const&)
|
||||
{
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body type requirements not met");
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
template<class T>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using is_body = __see_below__;
|
||||
#else
|
||||
using is_body = detail::has_value_type<T>;
|
||||
#endif
|
||||
|
||||
/** Determine if a type has a nested <em>BodyWriter</em>.
|
||||
|
||||
This alias template is `std::true_type` when:
|
||||
|
||||
@li `T` has a nested type named `writer`
|
||||
|
||||
@li `writer` meets the requirements of <em>BodyWriter</em>.
|
||||
|
||||
@tparam T The body type to test.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void check_can_serialize(message<isRequest, Body, Fields> const&)
|
||||
{
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"Cannot serialize Body, no reader");
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
using is_body_writer = __see_below__;
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_body_writer : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_body_writer<T, beast::detail::void_t<
|
||||
typename T::writer,
|
||||
typename T::writer::const_buffers_type,
|
||||
decltype(
|
||||
std::declval<typename T::writer&>().init(std::declval<error_code&>()),
|
||||
std::declval<boost::optional<std::pair<
|
||||
typename T::writer::const_buffers_type, bool>>&>() =
|
||||
std::declval<typename T::writer>().get(std::declval<error_code&>())
|
||||
)>> : std::integral_constant<bool,
|
||||
net::is_const_buffer_sequence<
|
||||
typename T::writer::const_buffers_type>::value && (
|
||||
(std::is_constructible<typename T::writer,
|
||||
header<true, detail::fields_model>&,
|
||||
typename T::value_type&>::value &&
|
||||
std::is_constructible<typename T::writer,
|
||||
header<false, detail::fields_model>&,
|
||||
typename T::value_type&>::value)
|
||||
)
|
||||
> {};
|
||||
#endif
|
||||
|
||||
/** Determine if a type has a nested <em>BodyWriter</em>.
|
||||
|
||||
This alias template is `std::true_type` when:
|
||||
|
||||
@li `T` has a nested type named `writer`
|
||||
|
||||
@li `writer` meets the requirements of <em>BodyWriter</em>.
|
||||
|
||||
@tparam T The body type to test.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
using is_mutable_body_writer = __see_below__;
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_mutable_body_writer : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_mutable_body_writer<T, beast::detail::void_t<
|
||||
typename T::writer,
|
||||
typename T::writer::const_buffers_type,
|
||||
decltype(
|
||||
std::declval<typename T::writer&>().init(std::declval<error_code&>()),
|
||||
std::declval<boost::optional<std::pair<
|
||||
typename T::writer::const_buffers_type, bool>>&>() =
|
||||
std::declval<typename T::writer>().get(std::declval<error_code&>())
|
||||
)>> : std::integral_constant<bool,
|
||||
net::is_const_buffer_sequence<
|
||||
typename T::writer::const_buffers_type>::value && ((
|
||||
std::is_constructible<typename T::writer,
|
||||
header<true, detail::fields_model>&,
|
||||
typename T::value_type&>::value &&
|
||||
std::is_constructible<typename T::writer,
|
||||
header<false, detail::fields_model>&,
|
||||
typename T::value_type&>::value &&
|
||||
! std::is_constructible<typename T::writer,
|
||||
header<true, detail::fields_model> const&,
|
||||
typename T::value_type const&>::value &&
|
||||
! std::is_constructible<typename T::writer,
|
||||
header<false, detail::fields_model> const&,
|
||||
typename T::value_type const&>::value
|
||||
))
|
||||
>{};
|
||||
#endif
|
||||
|
||||
/** Determine if a type has a nested <em>BodyReader</em>.
|
||||
|
||||
This alias template is `std::true_type` when:
|
||||
|
||||
@li `T` has a nested type named `reader`
|
||||
|
||||
@li `reader` meets the requirements of <em>BodyReader</em>.
|
||||
|
||||
@tparam T The body type to test.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void check_can_parse(message<isRequest, Body, Fields>&)
|
||||
{
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"Cannot parse Body, no reader");
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
using is_body_reader = __see_below__;
|
||||
#else
|
||||
template<class T, class = void>
|
||||
struct is_body_reader : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_body_reader<T, beast::detail::void_t<decltype(
|
||||
std::declval<typename T::reader&>().init(
|
||||
boost::optional<std::uint64_t>(),
|
||||
std::declval<error_code&>()),
|
||||
std::declval<std::size_t&>() =
|
||||
std::declval<typename T::reader&>().put(
|
||||
std::declval<net::const_buffer>(),
|
||||
std::declval<error_code&>()),
|
||||
std::declval<typename T::reader&>().finish(
|
||||
std::declval<error_code&>())
|
||||
)>> : std::integral_constant<bool,
|
||||
(std::is_constructible<typename T::reader,
|
||||
header<true, detail::fields_model>&,
|
||||
typename T::value_type&>::value &&
|
||||
std::is_constructible<typename T::reader,
|
||||
header<false,detail::fields_model>&,
|
||||
typename T::value_type&>::value)
|
||||
>
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Determine if a type meets the <em>Fields</em> named requirements.
|
||||
|
||||
This alias template is `std::true_type` if `T` meets
|
||||
the requirements, otherwise it is `std::false_type`.
|
||||
|
||||
@tparam T The type to test.
|
||||
|
||||
@par Example
|
||||
Use with `static_assert`:
|
||||
@code
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
void f(message<isRequest, Body, Fields> const&)
|
||||
{
|
||||
static_assert(is_fields<Fields>::value,
|
||||
"Fields type requirements not met");
|
||||
...
|
||||
@endcode
|
||||
|
||||
Use with `std::enable_if` (SFINAE):
|
||||
@code
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
typename std::enable_if<is_fields<Fields>::value>::type
|
||||
f(message<isRequest, Body, Fields> const&);
|
||||
@endcode
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
template<class T>
|
||||
using is_fields = __see_below__;
|
||||
#else
|
||||
template<class T>
|
||||
using is_fields = typename detail::is_fields_helper<T>::type;
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_VECTOR_BODY_HPP
|
||||
#define BOOST_BEAST_HTTP_VECTOR_BODY_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/buffer_traits.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/core/detail/clamp.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A <em>Body</em> using `std::vector`
|
||||
|
||||
This body uses `std::vector` as a memory-based container
|
||||
for holding message payloads. Messages using this body type
|
||||
may be serialized and parsed.
|
||||
*/
|
||||
template<class T, class Allocator = std::allocator<T>>
|
||||
struct vector_body
|
||||
{
|
||||
private:
|
||||
static_assert(sizeof(T) == 1,
|
||||
"T requirements not met");
|
||||
|
||||
public:
|
||||
/** The type of container used for the body
|
||||
|
||||
This determines the type of @ref message::body
|
||||
when this body type is used with a message container.
|
||||
*/
|
||||
using value_type = std::vector<T, Allocator>;
|
||||
|
||||
/** Returns the payload size of the body
|
||||
|
||||
When this body is used with @ref message::prepare_payload,
|
||||
the Content-Length will be set to the payload size, and
|
||||
any chunked Transfer-Encoding will be removed.
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& body)
|
||||
{
|
||||
return body.size();
|
||||
}
|
||||
|
||||
/** The algorithm for parsing the body
|
||||
|
||||
Meets the requirements of <em>BodyReader</em>.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using reader = __implementation_defined__;
|
||||
#else
|
||||
class reader
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
reader(header<isRequest, Fields>&, value_type& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> const& length, error_code& ec)
|
||||
{
|
||||
if(length)
|
||||
{
|
||||
if(*length > body_.max_size())
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::buffer_overflow);
|
||||
return;
|
||||
}
|
||||
body_.reserve(beast::detail::clamp(*length));
|
||||
}
|
||||
ec = {};
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
auto const n = buffer_bytes(buffers);
|
||||
auto const len = body_.size();
|
||||
if (n > body_.max_size() - len)
|
||||
{
|
||||
BOOST_BEAST_ASSIGN_EC(ec, error::buffer_overflow);
|
||||
return 0;
|
||||
}
|
||||
|
||||
body_.resize(len + n);
|
||||
ec = {};
|
||||
return net::buffer_copy(net::buffer(
|
||||
&body_[0] + len, n), buffers);
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** The algorithm for serializing the body
|
||||
|
||||
Meets the requirements of <em>BodyWriter</em>.
|
||||
*/
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
using writer = __implementation_defined__;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type const& body_;
|
||||
|
||||
public:
|
||||
using const_buffers_type =
|
||||
net::const_buffer;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(header<isRequest, Fields> const&, value_type const& b)
|
||||
: body_(b)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
}
|
||||
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
ec = {};
|
||||
return {{const_buffers_type{
|
||||
body_.data(), body_.size()}, false}};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#endif
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_VERB_HPP
|
||||
#define BOOST_BEAST_HTTP_VERB_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/core/string.hpp>
|
||||
#include <iosfwd>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** HTTP request method verbs
|
||||
|
||||
Each verb corresponds to a particular method string
|
||||
used in HTTP request messages.
|
||||
*/
|
||||
enum class verb
|
||||
{
|
||||
/** An unknown method.
|
||||
|
||||
This value indicates that the request method string is not
|
||||
one of the recognized verbs. Callers interested in the method
|
||||
should use an interface which returns the original string.
|
||||
*/
|
||||
unknown = 0,
|
||||
|
||||
/// The DELETE method deletes the specified resource
|
||||
delete_,
|
||||
|
||||
/** The GET method requests a representation of the specified resource.
|
||||
|
||||
Requests using GET should only retrieve data and should have no other effect.
|
||||
*/
|
||||
get,
|
||||
|
||||
/** The HEAD method asks for a response identical to that of a GET request, but without the response body.
|
||||
|
||||
This is useful for retrieving meta-information written in response
|
||||
headers, without having to transport the entire content.
|
||||
*/
|
||||
head,
|
||||
|
||||
/** The POST method requests that the server accept the entity enclosed in the request as a new subordinate of the web resource identified by the URI.
|
||||
|
||||
The data POSTed might be, for example, an annotation for existing
|
||||
resources; a message for a bulletin board, newsgroup, mailing list,
|
||||
or comment thread; a block of data that is the result of submitting
|
||||
a web form to a data-handling process; or an item to add to a database
|
||||
*/
|
||||
post,
|
||||
|
||||
/** The PUT method requests that the enclosed entity be stored under the supplied URI.
|
||||
|
||||
If the URI refers to an already existing resource, it is modified;
|
||||
if the URI does not point to an existing resource, then the server
|
||||
can create the resource with that URI.
|
||||
*/
|
||||
put,
|
||||
|
||||
/** The CONNECT method converts the request connection to a transparent TCP/IP tunnel.
|
||||
|
||||
This is usually to facilitate SSL-encrypted communication (HTTPS)
|
||||
through an unencrypted HTTP proxy.
|
||||
*/
|
||||
connect,
|
||||
|
||||
/** The OPTIONS method returns the HTTP methods that the server supports for the specified URL.
|
||||
|
||||
This can be used to check the functionality of a web server by requesting
|
||||
'*' instead of a specific resource.
|
||||
*/
|
||||
options,
|
||||
|
||||
/** The TRACE method echoes the received request so that a client can see what (if any) changes or additions have been made by intermediate servers.
|
||||
*/
|
||||
trace,
|
||||
|
||||
// WebDAV
|
||||
|
||||
copy,
|
||||
lock,
|
||||
mkcol,
|
||||
move,
|
||||
propfind,
|
||||
proppatch,
|
||||
search,
|
||||
unlock,
|
||||
bind,
|
||||
rebind,
|
||||
unbind,
|
||||
acl,
|
||||
|
||||
// subversion
|
||||
|
||||
report,
|
||||
mkactivity,
|
||||
checkout,
|
||||
merge,
|
||||
|
||||
// upnp
|
||||
|
||||
msearch,
|
||||
notify,
|
||||
subscribe,
|
||||
unsubscribe,
|
||||
|
||||
// RFC-5789
|
||||
|
||||
patch,
|
||||
purge,
|
||||
|
||||
// CalDAV
|
||||
|
||||
mkcalendar,
|
||||
|
||||
// RFC-2068, section 19.6.1.2
|
||||
|
||||
link,
|
||||
unlink
|
||||
};
|
||||
|
||||
/** Converts a string to the request method verb.
|
||||
|
||||
If the string does not match a known request method,
|
||||
@ref verb::unknown is returned.
|
||||
*/
|
||||
BOOST_BEAST_DECL
|
||||
verb
|
||||
string_to_verb(string_view s);
|
||||
|
||||
/// Returns the text representation of a request method verb.
|
||||
BOOST_BEAST_DECL
|
||||
string_view
|
||||
to_string(verb v);
|
||||
|
||||
/// Write the text for a request method verb to an output stream.
|
||||
inline
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, verb v)
|
||||
{
|
||||
return os << to_string(v);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#ifdef BOOST_BEAST_HEADER_ONLY
|
||||
#include <boost/beast/http/impl/verb.ipp>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+828
@@ -0,0 +1,828 @@
|
||||
//
|
||||
// Copyright (c) 2016-2019 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)
|
||||
//
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#ifndef BOOST_BEAST_HTTP_WRITE_HPP
|
||||
#define BOOST_BEAST_HTTP_WRITE_HPP
|
||||
|
||||
#include <boost/beast/core/detail/config.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/serializer.hpp>
|
||||
#include <boost/beast/http/type_traits.hpp>
|
||||
#include <boost/beast/http/detail/chunk_encode.hpp>
|
||||
#include <boost/beast/core/error.hpp>
|
||||
#include <boost/beast/core/stream_traits.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <iosfwd>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace boost {
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Write part of a message to a stream using a serializer.
|
||||
|
||||
This function is used to write part of a message to a stream using
|
||||
a caller-provided HTTP/1 serializer. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li One or more bytes have been transferred.
|
||||
|
||||
@li The function @ref serializer::is_done returns `true`
|
||||
|
||||
@li An error occurs on the stream.
|
||||
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
The amount of data actually transferred is controlled by the behavior
|
||||
of the underlying stream, subject to the buffer size limit of the
|
||||
serializer obtained or set through a call to @ref serializer::limit.
|
||||
Setting a limit and performing bounded work helps applications set
|
||||
reasonable timeouts. It also allows application-level flow control
|
||||
to function correctly. For example when using a TCP/IP based
|
||||
stream.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>SyncWriteStream</em> concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@return The number of bytes written to the stream.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@see serializer
|
||||
*/
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_some(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr);
|
||||
|
||||
/** Write part of a message to a stream using a serializer.
|
||||
|
||||
This function is used to write part of a message to a stream using
|
||||
a caller-provided HTTP/1 serializer. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li One or more bytes have been transferred.
|
||||
|
||||
@li The function @ref serializer::is_done returns `true`
|
||||
|
||||
@li An error occurs on the stream.
|
||||
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
The amount of data actually transferred is controlled by the behavior
|
||||
of the underlying stream, subject to the buffer size limit of the
|
||||
serializer obtained or set through a call to @ref serializer::limit.
|
||||
Setting a limit and performing bounded work helps applications set
|
||||
reasonable timeouts. It also allows application-level flow control
|
||||
to function correctly. For example when using a TCP/IP based
|
||||
stream.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>SyncWriteStream</em> concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@param ec Set to indicate what error occurred, if any.
|
||||
|
||||
@return The number of bytes written to the stream.
|
||||
|
||||
@see async_write_some, serializer
|
||||
*/
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_some(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec);
|
||||
|
||||
/** Write part of a message to a stream asynchronously using a serializer.
|
||||
|
||||
This function is used to write part of a message to a stream
|
||||
asynchronously using a caller-provided HTTP/1 serializer. The function
|
||||
call always returns immediately. The asynchronous operation will continue
|
||||
until one of the following conditions is true:
|
||||
|
||||
@li One or more bytes have been transferred.
|
||||
|
||||
@li The function @ref serializer::is_done returns `true`
|
||||
|
||||
@li An error occurs on the stream.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the stream's
|
||||
`async_write_some` function, and is known as a <em>composed operation</em>.
|
||||
The program must ensure that the stream performs no other writes
|
||||
until this operation completes.
|
||||
|
||||
The amount of data actually transferred is controlled by the behavior
|
||||
of the underlying stream, subject to the buffer size limit of the
|
||||
serializer obtained or set through a call to @ref serializer::limit.
|
||||
Setting a limit and performing bounded work helps applications set
|
||||
reasonable timeouts. It also allows application-level flow control
|
||||
to function correctly. For example when using a TCP/IP based
|
||||
stream.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>AsyncWriteStream</em> concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
The object must remain valid at least until the
|
||||
handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The completion handler to invoke when the operation
|
||||
completes. The implementation takes ownership of the handler by
|
||||
performing a decay-copy. The equivalent function signature of
|
||||
the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
error_code const& error, // result of operation
|
||||
std::size_t bytes_transferred // the number of bytes written to the stream
|
||||
);
|
||||
@endcode
|
||||
If the handler has an associated immediate executor,
|
||||
an immediate completion will be dispatched to it.
|
||||
Otherwise, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
@see serializer
|
||||
*/
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncWriteStream>>>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write_some(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncWriteStream>>{});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Write a header to a stream using a serializer.
|
||||
|
||||
This function is used to write a header to a stream using a
|
||||
caller-provided HTTP/1 serializer. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li The function @ref serializer::is_header_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>SyncWriteStream</em> concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@return The number of bytes written to the stream.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@note The implementation will call @ref serializer::split with
|
||||
the value `true` on the serializer passed in.
|
||||
|
||||
@see serializer
|
||||
*/
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_header(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr);
|
||||
|
||||
/** Write a header to a stream using a serializer.
|
||||
|
||||
This function is used to write a header to a stream using a
|
||||
caller-provided HTTP/1 serializer. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li The function @ref serializer::is_header_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>SyncWriteStream</em> concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@param ec Set to indicate what error occurred, if any.
|
||||
|
||||
@return The number of bytes written to the stream.
|
||||
|
||||
@note The implementation will call @ref serializer::split with
|
||||
the value `true` on the serializer passed in.
|
||||
|
||||
@see serializer
|
||||
*/
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write_header(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec);
|
||||
|
||||
/** Write a header to a stream asynchronously using a serializer.
|
||||
|
||||
This function is used to write a header to a stream asynchronously
|
||||
using a caller-provided HTTP/1 serializer. The function call always
|
||||
returns immediately. The asynchronous operation will continue until
|
||||
one of the following conditions is true:
|
||||
|
||||
@li The function @ref serializer::is_header_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the stream's
|
||||
`async_write_some` function, and is known as a <em>composed operation</em>.
|
||||
The program must ensure that the stream performs no other writes
|
||||
until this operation completes.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>AsyncWriteStream</em> concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
The object must remain valid at least until the
|
||||
handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The completion handler to invoke when the operation
|
||||
completes. The implementation takes ownership of the handler by
|
||||
performing a decay-copy. The equivalent function signature of
|
||||
the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
error_code const& error, // result of operation
|
||||
std::size_t bytes_transferred // the number of bytes written to the stream
|
||||
);
|
||||
@endcode
|
||||
If the handler has an associated immediate executor,
|
||||
an immediate completion will be dispatched to it.
|
||||
Otherwise, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@note The implementation will call @ref serializer::split with
|
||||
the value `true` on the serializer passed in.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
@see serializer
|
||||
*/
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncWriteStream>>>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write_header(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncWriteStream>>{});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Write a complete message to a stream using a serializer.
|
||||
|
||||
This function is used to write a complete message to a stream using
|
||||
a caller-provided HTTP/1 serializer. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li The function @ref serializer::is_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>SyncWriteStream</em> concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@return The number of bytes written to the stream.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@see serializer
|
||||
*/
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr);
|
||||
|
||||
/** Write a complete message to a stream using a serializer.
|
||||
|
||||
This function is used to write a complete message to a stream using
|
||||
a caller-provided HTTP/1 serializer. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li The function @ref serializer::is_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>SyncWriteStream</em> concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of bytes written to the stream.
|
||||
|
||||
@see serializer
|
||||
*/
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
std::size_t
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
error_code& ec);
|
||||
|
||||
/** Write a complete message to a stream asynchronously using a serializer.
|
||||
|
||||
This function is used to write a complete message to a stream
|
||||
asynchronously using a caller-provided HTTP/1 serializer. The
|
||||
function call always returns immediately. The asynchronous
|
||||
operation will continue until one of the following conditions is true:
|
||||
|
||||
@li The function @ref serializer::is_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the stream's
|
||||
`async_write_some` function, and is known as a <em>composed operation</em>.
|
||||
The program must ensure that the stream performs no other writes
|
||||
until this operation completes.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>AsyncWriteStream</em> concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
The object must remain valid at least until the
|
||||
handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The completion handler to invoke when the operation
|
||||
completes. The implementation takes ownership of the handler by
|
||||
performing a decay-copy. The equivalent function signature of
|
||||
the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
error_code const& error, // result of operation
|
||||
std::size_t bytes_transferred // the number of bytes written to the stream
|
||||
);
|
||||
@endcode
|
||||
If the handler has an associated immediate executor,
|
||||
an immediate completion will be dispatched to it.
|
||||
Otherwise, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
@see serializer
|
||||
*/
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncWriteStream>>>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
serializer<isRequest, Body, Fields>& sr,
|
||||
WriteHandler&& handler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncWriteStream>>{});
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Write a complete message to a stream.
|
||||
|
||||
This function is used to write a complete message to a stream using
|
||||
HTTP/1. The call will block until one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`write_some` function. The algorithm will use a temporary @ref serializer
|
||||
with an empty chunk decorator to produce buffers.
|
||||
|
||||
@note This function only participates in overload resolution
|
||||
if @ref is_mutable_body_writer for <em>Body</em> returns `true`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>SyncWriteStream</em> concept.
|
||||
|
||||
@param msg The message to write.
|
||||
|
||||
@return The number of bytes written to the stream.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@see message
|
||||
*/
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
std::size_t
|
||||
#else
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
#endif
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg);
|
||||
|
||||
/** Write a complete message to a stream.
|
||||
|
||||
This function is used to write a complete message to a stream using
|
||||
HTTP/1. The call will block until one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`write_some` function. The algorithm will use a temporary @ref serializer
|
||||
with an empty chunk decorator to produce buffers.
|
||||
|
||||
@note This function only participates in overload resolution
|
||||
if @ref is_mutable_body_writer for <em>Body</em> returns `false`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>SyncWriteStream</em> concept.
|
||||
|
||||
@param msg The message to write.
|
||||
|
||||
@return The number of bytes written to the stream.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@see message
|
||||
*/
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
std::size_t
|
||||
#else
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
#endif
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg);
|
||||
|
||||
/** Write a complete message to a stream.
|
||||
|
||||
This function is used to write a complete message to a stream using
|
||||
HTTP/1. The call will block until one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`write_some` function. The algorithm will use a temporary @ref serializer
|
||||
with an empty chunk decorator to produce buffers.
|
||||
|
||||
@note This function only participates in overload resolution
|
||||
if @ref is_mutable_body_writer for <em>Body</em> returns `true`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>SyncWriteStream</em> concept.
|
||||
|
||||
@param msg The message to write.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of bytes written to the stream.
|
||||
|
||||
@see message
|
||||
*/
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
std::size_t
|
||||
#else
|
||||
typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
#endif
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Write a complete message to a stream.
|
||||
|
||||
This function is used to write a complete message to a stream using
|
||||
HTTP/1. The call will block until one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`write_some` function. The algorithm will use a temporary @ref serializer
|
||||
with an empty chunk decorator to produce buffers.
|
||||
|
||||
@note This function only participates in overload resolution
|
||||
if @ref is_mutable_body_writer for <em>Body</em> returns `false`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>SyncWriteStream</em> concept.
|
||||
|
||||
@param msg The message to write.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of bytes written to the stream.
|
||||
|
||||
@see message
|
||||
*/
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
#if BOOST_BEAST_DOXYGEN
|
||||
std::size_t
|
||||
#else
|
||||
typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value,
|
||||
std::size_t>::type
|
||||
#endif
|
||||
write(
|
||||
SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Write a complete message to a stream asynchronously.
|
||||
|
||||
This function is used to write a complete message to a stream asynchronously
|
||||
using HTTP/1. The function call always returns immediately. The asynchronous
|
||||
operation will continue until one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the stream's
|
||||
`async_write_some` function, and is known as a <em>composed operation</em>.
|
||||
The program must ensure that the stream performs no other writes
|
||||
until this operation completes. The algorithm will use a temporary
|
||||
@ref serializer with an empty chunk decorator to produce buffers.
|
||||
|
||||
@note This function only participates in overload resolution
|
||||
if @ref is_mutable_body_writer for <em>Body</em> returns `true`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>AsyncWriteStream</em> concept.
|
||||
|
||||
@param msg The message to write.
|
||||
The object must remain valid at least until the
|
||||
handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The completion handler to invoke when the operation
|
||||
completes. The implementation takes ownership of the handler by
|
||||
performing a decay-copy. The equivalent function signature of
|
||||
the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
error_code const& error, // result of operation
|
||||
std::size_t bytes_transferred // the number of bytes written to the stream
|
||||
);
|
||||
@endcode
|
||||
If the handler has an associated immediate executor,
|
||||
an immediate completion will be dispatched to it.
|
||||
Otherwise, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
@see message
|
||||
*/
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncWriteStream>>>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
WriteHandler&& handler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncWriteStream>>{}
|
||||
#ifndef BOOST_BEAST_DOXYGEN
|
||||
, typename std::enable_if<
|
||||
is_mutable_body_writer<Body>::value>::type* = 0
|
||||
#endif
|
||||
);
|
||||
|
||||
/** Write a complete message to a stream asynchronously.
|
||||
|
||||
This function is used to write a complete message to a stream asynchronously
|
||||
using HTTP/1. The function call always returns immediately. The asynchronous
|
||||
operation will continue until one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the stream's
|
||||
`async_write_some` function, and is known as a <em>composed operation</em>.
|
||||
The program must ensure that the stream performs no other writes
|
||||
until this operation completes. The algorithm will use a temporary
|
||||
@ref serializer with an empty chunk decorator to produce buffers.
|
||||
|
||||
@note This function only participates in overload resolution
|
||||
if @ref is_mutable_body_writer for <em>Body</em> returns `false`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the <em>AsyncWriteStream</em> concept.
|
||||
|
||||
@param msg The message to write.
|
||||
The object must remain valid at least until the
|
||||
handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The completion handler to invoke when the operation
|
||||
completes. The implementation takes ownership of the handler by
|
||||
performing a decay-copy. The equivalent function signature of
|
||||
the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
error_code const& error, // result of operation
|
||||
std::size_t bytes_transferred // the number of bytes written to the stream
|
||||
);
|
||||
@endcode
|
||||
If the handler has an associated immediate executor,
|
||||
an immediate completion will be dispatched to it.
|
||||
Otherwise, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `net::post`.
|
||||
|
||||
@par Per-Operation Cancellation
|
||||
|
||||
This asynchronous operation supports cancellation for the following
|
||||
net::cancellation_type values:
|
||||
|
||||
@li @c net::cancellation_type::terminal
|
||||
|
||||
if the `stream` also supports terminal cancellation.
|
||||
|
||||
`terminal` cancellation leaves the stream in an undefined state,
|
||||
so that only closing it is guaranteed to succeed.
|
||||
|
||||
@see message
|
||||
*/
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
BOOST_BEAST_ASYNC_TPARAM2 WriteHandler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncWriteStream>>>
|
||||
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
|
||||
async_write(
|
||||
AsyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
WriteHandler&& handler =
|
||||
net::default_completion_token_t<
|
||||
executor_type<AsyncWriteStream>>{}
|
||||
#ifndef BOOST_BEAST_DOXYGEN
|
||||
, typename std::enable_if<
|
||||
! is_mutable_body_writer<Body>::value>::type* = 0
|
||||
#endif
|
||||
);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Serialize an HTTP/1 header to a `std::ostream`.
|
||||
|
||||
The function converts the header to its HTTP/1 serialized
|
||||
representation and stores the result in the output stream.
|
||||
|
||||
@param os The output stream to write to.
|
||||
|
||||
@param msg The message fields to write.
|
||||
*/
|
||||
template<bool isRequest, class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
header<isRequest, Fields> const& msg);
|
||||
|
||||
/** Serialize an HTTP/1 message to a `std::ostream`.
|
||||
|
||||
The function converts the message to its HTTP/1 serialized
|
||||
representation and stores the result in the output stream.
|
||||
|
||||
The implementation will automatically perform chunk encoding if
|
||||
the contents of the message indicate that chunk encoding is required.
|
||||
|
||||
@param os The output stream to write to.
|
||||
|
||||
@param msg The message to write.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os,
|
||||
message<isRequest, Body, Fields> const& msg);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
} // boost
|
||||
|
||||
#include <boost/beast/http/impl/write.hpp>
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user