Added thirdparty: boost library

This commit is contained in:
Viacheslav Demydiuk
2024-01-06 19:55:56 +02:00
parent bf49f439e1
commit bccd1e7051
15683 changed files with 3239840 additions and 0 deletions
+161
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
File diff suppressed because it is too large Load Diff
+138
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+99
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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