forked from boostorg/beast
Refactor HTTP serialization and parsing (API Change):
fix #404 Parsing and serialization interfaces have been fine tuned and unified. For parsing these stream operations are defined: * read * read_header * read_some * async_read * async_read_header * async_read_some For serialization these stream operations are defined: * write * write_header * write_some * async_write * async_write_header * async_write_some
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
||||
@@ -24,9 +25,13 @@ read_size_helper(DynamicBuffer const& buffer, std::size_t max_size)
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(max_size >= 1);
|
||||
auto const size = buffer.size();
|
||||
return std::min<std::size_t>(
|
||||
std::max<std::size_t>(512, buffer.capacity() - size),
|
||||
std::min<std::size_t>(max_size, buffer.max_size() - size));
|
||||
auto const limit = buffer.max_size() - size;
|
||||
if(limit > 0)
|
||||
return std::min<std::size_t>(
|
||||
std::max<std::size_t>(512, buffer.capacity() - size),
|
||||
std::min<std::size_t>(max_size, limit));
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"dynamic buffer overflow"});
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
@@ -80,7 +80,7 @@ write_some(ConstBufferSequence const& buffers,
|
||||
buffer_size(buffer));
|
||||
if(os_.fail())
|
||||
{
|
||||
ec = errc::make_error_code(
|
||||
ec = make_error_code(
|
||||
errc::no_stream_resources);
|
||||
break;
|
||||
}
|
||||
|
||||
+189
-351
@@ -21,41 +21,6 @@
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Describes the parser's current state.
|
||||
|
||||
The state is expressed as the type of data that
|
||||
@ref basic_parser is expecting to see in subsequently
|
||||
provided octets.
|
||||
*/
|
||||
enum class parse_state
|
||||
{
|
||||
/// Expecting one or more header octets
|
||||
header = 0,
|
||||
|
||||
/// Expecting one or more body octets
|
||||
body = 1,
|
||||
|
||||
/// Expecting zero or more body octets followed by EOF
|
||||
body_to_eof = 2,
|
||||
|
||||
/// Expecting additional chunk header octets
|
||||
chunk_header = 3,
|
||||
|
||||
/// Expecting one or more chunk body octets
|
||||
chunk_body = 4,
|
||||
|
||||
/** The parsing is complete.
|
||||
|
||||
The parse is considered complete when the full header
|
||||
is received and either the full body is received, or
|
||||
the semantics of the message indicate that no body
|
||||
is expected. This includes the case where the caller
|
||||
has indicated to the parser that no body is expected,
|
||||
for example when receiving a response to a HEAD request.
|
||||
*/
|
||||
complete = 5
|
||||
};
|
||||
|
||||
/** A parser for decoding HTTP/1 wire format messages.
|
||||
|
||||
This parser is designed to efficiently parse messages in the
|
||||
@@ -83,18 +48,13 @@ enum class parse_state
|
||||
struct derived
|
||||
: basic_parser<isRequest, derived<isRequest>>
|
||||
{
|
||||
// The type used when providing a mutable
|
||||
// buffer sequence in which to store body data.
|
||||
//
|
||||
using mutable_buffers_type = ...;
|
||||
|
||||
// When isRequest == true, called
|
||||
// after the Request Line is received.
|
||||
//
|
||||
void
|
||||
on_request(
|
||||
string_view const& method,
|
||||
string_view const& target,
|
||||
string_view method,
|
||||
string_view target,
|
||||
int version,
|
||||
error_code& ec);
|
||||
|
||||
@@ -104,7 +64,7 @@ enum class parse_state
|
||||
void
|
||||
on_response(
|
||||
int status,
|
||||
string_view const& reason,
|
||||
string_view reason,
|
||||
int version,
|
||||
error_code& ec);
|
||||
|
||||
@@ -112,8 +72,8 @@ enum class parse_state
|
||||
//
|
||||
void
|
||||
on_field(
|
||||
string_view const& name,
|
||||
string_view const& value,
|
||||
string_view name,
|
||||
string_view value,
|
||||
error_code& ec);
|
||||
|
||||
// Called after the header is complete.
|
||||
@@ -123,40 +83,19 @@ enum class parse_state
|
||||
error_code& ec);
|
||||
|
||||
// Called once before the body, if any, is started.
|
||||
// This will only be called if the semantics of the
|
||||
// message indicate that a body exists, including
|
||||
// an indicated body of zero length.
|
||||
//
|
||||
void
|
||||
on_body();
|
||||
on_body(
|
||||
boost::optional<std::uint64_t> content_length,
|
||||
error_code& ec);
|
||||
|
||||
// Called zero or more times to provide body data.
|
||||
//
|
||||
// Only used if isDirect == false
|
||||
//
|
||||
void
|
||||
on_data(
|
||||
string_view const& s,
|
||||
string_view s,
|
||||
error_code& ec);
|
||||
|
||||
// Called zero or more times to retrieve a mutable
|
||||
// buffer sequence in which to store body data.
|
||||
//
|
||||
// Only used if isDirect == true
|
||||
//
|
||||
mutable_buffers_type
|
||||
on_prepare(
|
||||
std::size_t n);
|
||||
|
||||
// Called after body data has been stored in the
|
||||
// buffer returned by the previous call to on_prepare.
|
||||
//
|
||||
// Only used if isDirect == true
|
||||
//
|
||||
void
|
||||
on_commit(
|
||||
std::size_t n);
|
||||
|
||||
// If the Transfer-Encoding is specified, and the
|
||||
// last item in the list of encodings is "chunked",
|
||||
// called after receiving a chunk header or a final
|
||||
@@ -164,8 +103,8 @@ enum class parse_state
|
||||
//
|
||||
void
|
||||
on_chunk(
|
||||
std::uint64_t length, // Length of this chunk
|
||||
string_view const& ext, // The chunk extensions, if any
|
||||
std::uint64_t length, // Length of this chunk
|
||||
string_view const& ext, // The chunk extensions, if any
|
||||
error_code& ec);
|
||||
|
||||
// Called once when the message is complete.
|
||||
@@ -177,16 +116,7 @@ enum class parse_state
|
||||
@endcode
|
||||
|
||||
If a callback sets the error code, the error will be propagated
|
||||
to the caller of the parser. Behavior of parsing after an error
|
||||
is returned is undefined.
|
||||
|
||||
When the parser state is positioned to read bytes belonging to
|
||||
the body, calling @ref write or @ref write will implicitly
|
||||
cause a buffer copy (because bytes are first transferred to the
|
||||
dynamic buffer). To avoid this copy, the additional functions
|
||||
@ref copy_body, @ref prepare_body, and @ref commit_body are
|
||||
provided to allow the caller to read bytes directly into buffers
|
||||
supplied by the parser.
|
||||
to the caller of the parser.
|
||||
|
||||
The parser is optimized for the case where the input buffer
|
||||
sequence consists of a single contiguous buffer. The
|
||||
@@ -203,26 +133,21 @@ enum class parse_state
|
||||
@tparam isRequest A `bool` indicating whether the parser will be
|
||||
presented with request or response message.
|
||||
|
||||
@tparam isDirect A `bool` indicating whether the parser interface
|
||||
supports reading body data directly into parser-provided buffers.
|
||||
|
||||
@tparam Derived The derived class type. This is part of the
|
||||
Curiously Recurring Template Pattern interface.
|
||||
*/
|
||||
template<bool isRequest, bool isDirect, class Derived>
|
||||
template<bool isRequest, class Derived>
|
||||
class basic_parser
|
||||
: private detail::basic_parser_base
|
||||
{
|
||||
template<bool OtherIsRequest,
|
||||
bool OtherIsDirect, class OtherDerived>
|
||||
template<bool OtherIsRequest, class OtherDerived>
|
||||
friend class basic_parser;
|
||||
|
||||
// Message will be complete after reading header
|
||||
static unsigned constexpr flagSkipBody = 1<< 0;
|
||||
|
||||
|
||||
|
||||
static unsigned constexpr flagOnBody = 1<< 1;
|
||||
// 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;
|
||||
@@ -248,10 +173,8 @@ class basic_parser
|
||||
std::size_t buf_len_ = 0;
|
||||
std::size_t skip_ = 0; // search from here
|
||||
std::size_t x_; // scratch variable
|
||||
state state_ = state::nothing_yet;
|
||||
unsigned f_ = 0; // flags
|
||||
parse_state state_ = parse_state::header;
|
||||
string_view ext_;
|
||||
string_view body_;
|
||||
|
||||
public:
|
||||
/// Copy constructor (disallowed)
|
||||
@@ -264,7 +187,8 @@ public:
|
||||
basic_parser() = default;
|
||||
|
||||
/// `true` if this parser parses requests, `false` for responses.
|
||||
static bool constexpr is_request = isRequest;
|
||||
using is_request =
|
||||
std::integral_constant<bool, isRequest>;
|
||||
|
||||
/// Destructor
|
||||
~basic_parser() = default;
|
||||
@@ -274,83 +198,70 @@ public:
|
||||
After the move, the only valid operation on the
|
||||
moved-from object is destruction.
|
||||
*/
|
||||
template<bool OtherIsDirect, class OtherDerived>
|
||||
basic_parser(basic_parser<
|
||||
isRequest, OtherIsDirect, OtherDerived>&&);
|
||||
template<class OtherDerived>
|
||||
basic_parser(basic_parser<isRequest, OtherDerived>&&);
|
||||
|
||||
/** Set the skip body option.
|
||||
/** Returns a reference to this object as a @ref basic_parser.
|
||||
|
||||
The 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.
|
||||
|
||||
Depending on the request, some responses do not carry a body.
|
||||
For example, a 200 response to a CONNECT request from a
|
||||
tunneling proxy. 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.
|
||||
|
||||
@note This function must called before any bytes are processed.
|
||||
This is used to pass a derived class where a base class is
|
||||
expected, to choose a correct function overload when the
|
||||
resolution would be ambiguous.
|
||||
*/
|
||||
void
|
||||
skip_body();
|
||||
|
||||
/** Returns the current parser state.
|
||||
|
||||
The parser state indicates what octets the parser
|
||||
expects to see next in the input stream.
|
||||
*/
|
||||
parse_state
|
||||
state() const
|
||||
basic_parser&
|
||||
base()
|
||||
{
|
||||
return state_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns a constant reference to this object as a @ref basic_parser.
|
||||
|
||||
This is used to pass a derived class where a base class is
|
||||
expected, to choose a correct function overload when the
|
||||
resolution would be ambiguous.
|
||||
*/
|
||||
basic_parser const&
|
||||
base() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns `true` if the parser has received at least one byte of input.
|
||||
bool
|
||||
got_some() const
|
||||
{
|
||||
return (f_ & flagGotSome) != 0;
|
||||
}
|
||||
|
||||
/// Returns `true` if the complete header has been parsed.
|
||||
bool
|
||||
got_header() const
|
||||
{
|
||||
return state_ != parse_state::header;
|
||||
}
|
||||
|
||||
/** Returns `true` if a Content-Length is specified.
|
||||
|
||||
@note Only valid after parsing a complete header.
|
||||
*/
|
||||
bool
|
||||
got_content_length() const
|
||||
{
|
||||
return (f_ & flagContentLength) != 0;
|
||||
return state_ != state::nothing_yet;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is complete.
|
||||
|
||||
The message is complete after a full header is
|
||||
parsed and one of the following is true:
|
||||
The message is complete after the full header is prduced
|
||||
and one of the following is true:
|
||||
|
||||
@li @ref skip_body was called
|
||||
@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 received.
|
||||
@li The semantics of the message indicate a body is expected,
|
||||
and the entire body was parsed.
|
||||
*/
|
||||
bool
|
||||
is_complete() const
|
||||
is_done() const
|
||||
{
|
||||
return state_ == parse_state::complete;
|
||||
return state_ == state::complete;
|
||||
}
|
||||
|
||||
/** Returns `true` if a the parser has produced the full header.
|
||||
*/
|
||||
bool
|
||||
is_header_done() const
|
||||
{
|
||||
return state_ > state::header;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is an upgrade message.
|
||||
|
||||
@note Only valid after parsing a complete header.
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
is_upgrade() const
|
||||
@@ -358,16 +269,10 @@ public:
|
||||
return (f_ & flagConnectionUpgrade) != 0;
|
||||
}
|
||||
|
||||
/** Returns `true` if keep-alive is specified
|
||||
/** Returns `true` if the last value for Transfer-Encoding is "chunked".
|
||||
|
||||
@note Only valid after parsing a complete header.
|
||||
*/
|
||||
bool
|
||||
is_keep_alive() const;
|
||||
|
||||
/** Returns `true` if the chunked Transfer-Encoding is specified.
|
||||
|
||||
@note Only valid after parsing a complete header.
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
is_chunked() const
|
||||
@@ -375,34 +280,125 @@ public:
|
||||
return (f_ & flagChunked) != 0;
|
||||
}
|
||||
|
||||
/** Write part of a buffer sequence to the parser.
|
||||
/** Returns `true` if the message has keep-alive connection semantics.
|
||||
|
||||
This function attempts to parse the HTTP message
|
||||
stored in the caller provided buffers. Upon success,
|
||||
a positive return value indicates that the parser
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
is_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 `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;
|
||||
}
|
||||
|
||||
/// Returns `true` if the eager parse option is set.
|
||||
bool
|
||||
eager() const
|
||||
{
|
||||
return (f_ & flagEager) != 0;
|
||||
}
|
||||
|
||||
/** Set the eager parse option.
|
||||
|
||||
This option controls whether or not the parser will attempt
|
||||
to consume all of the octets provided during parsing, or
|
||||
if it will stop when it reaches one of the following positions
|
||||
within the serialized message:
|
||||
|
||||
@li Immediately after the header
|
||||
|
||||
@li Immediately after any chunk header
|
||||
|
||||
The default is to stop after the header or any chunk header.
|
||||
*/
|
||||
void
|
||||
eager(bool v)
|
||||
{
|
||||
if(v)
|
||||
f_ |= flagEager;
|
||||
else
|
||||
f_ &= ~flagEager;
|
||||
}
|
||||
|
||||
/// Returns `true` if the parser will ignore the message body.
|
||||
bool
|
||||
skip()
|
||||
{
|
||||
return (f_ & flagSkipBody) != 0;
|
||||
}
|
||||
|
||||
/** Set the skip body option.
|
||||
|
||||
The 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.
|
||||
|
||||
Depending on the request, some responses do not carry a
|
||||
body. For example, a 200 response to a CONNECT request
|
||||
from a tunneling proxy. 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.
|
||||
|
||||
@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.
|
||||
|
||||
A return value of zero indicates that the parser
|
||||
requires additional input. In this case the caller
|
||||
should append additional bytes to the input buffer
|
||||
sequence and call @ref write again.
|
||||
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 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
|
||||
@b ConstBufferSequence that represents the message.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of bytes consumed in the buffer
|
||||
sequence.
|
||||
@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
|
||||
write(ConstBufferSequence const& buffers, error_code& ec);
|
||||
put(ConstBufferSequence const& buffers, error_code& ec);
|
||||
|
||||
#if ! BEAST_DOXYGEN
|
||||
std::size_t
|
||||
write(boost::asio::const_buffers_1 const& buffer,
|
||||
put(boost::asio::const_buffers_1 const& buffer,
|
||||
error_code& ec);
|
||||
#endif
|
||||
|
||||
@@ -423,139 +419,7 @@ public:
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
void
|
||||
write_eof(error_code& ec);
|
||||
|
||||
/** Returns the number of bytes remaining in the body or chunk.
|
||||
|
||||
If a Content-Length is specified and the parser state
|
||||
is equal to @ref beast::http::parse_state::body, this will return
|
||||
the number of bytes remaining in the body. If the
|
||||
chunked Transfer-Encoding is indicated and the parser
|
||||
state is equal to @ref beast::http::parse_state::chunk_body, this
|
||||
will return the number of bytes remaining in the chunk.
|
||||
Otherwise, the function behavior is undefined.
|
||||
*/
|
||||
std::uint64_t
|
||||
size() const
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
state_ == parse_state::body ||
|
||||
state_ == parse_state::chunk_body);
|
||||
return len_;
|
||||
}
|
||||
|
||||
/** Returns the body data parsed in the last call to @ref write.
|
||||
|
||||
This buffer is invalidated after any call to @ref write
|
||||
or @ref write_eof.
|
||||
|
||||
@note If the last call to @ref write came from the input
|
||||
area of a @b DynamicBuffer object, a call to the dynamic
|
||||
buffer's `consume` function may invalidate this return
|
||||
value.
|
||||
*/
|
||||
string_view const&
|
||||
body() const
|
||||
{
|
||||
// This function not available when isDirect==true
|
||||
BOOST_STATIC_ASSERT(! isDirect);
|
||||
return body_;
|
||||
}
|
||||
|
||||
/** Returns the chunk extension parsed in the last call to @ref write.
|
||||
|
||||
This buffer is invalidated after any call to @ref write
|
||||
or @ref write_eof.
|
||||
|
||||
@note If the last call to @ref write came from the input
|
||||
area of a @b DynamicBuffer object, a call to the dynamic
|
||||
buffer's `consume` function may invalidate this return
|
||||
value.
|
||||
*/
|
||||
string_view const&
|
||||
chunk_extension() const
|
||||
{
|
||||
// This function not available when isDirect==true
|
||||
BOOST_STATIC_ASSERT(! isDirect);
|
||||
return ext_;
|
||||
}
|
||||
|
||||
/** Returns the optional value of Content-Length if known.
|
||||
|
||||
@note The return value is undefined unless a complete
|
||||
header has been parsed.
|
||||
*/
|
||||
boost::optional<std::uint64_t>
|
||||
content_length() const
|
||||
{
|
||||
BOOST_ASSERT(got_header());
|
||||
if(! (f_ & flagContentLength))
|
||||
return boost::none;
|
||||
return len_;
|
||||
}
|
||||
|
||||
/** Copy leftover body data from the dynamic buffer.
|
||||
|
||||
@note This member function is only available when
|
||||
`isDirect==true`.
|
||||
|
||||
@return The number of bytes processed from the dynamic
|
||||
buffer. The caller should remove these bytes by calling
|
||||
`consume` on the buffer.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
copy_body(DynamicBuffer& buffer);
|
||||
|
||||
/** Returns a set of buffers for storing body data.
|
||||
|
||||
@param buffers A writable output parameter into which
|
||||
the function will place the buffers upon success.
|
||||
|
||||
@param limit The maximum number of bytes in the
|
||||
size of the returned buffer sequence. The actual size
|
||||
of the buffer sequence may be lower than this number.
|
||||
|
||||
@note This member function is only available when
|
||||
`isDirect==true`.
|
||||
*/
|
||||
template<class MutableBufferSequence>
|
||||
void
|
||||
prepare_body(boost::optional<
|
||||
MutableBufferSequence>& buffers, std::size_t limit);
|
||||
|
||||
/** Commit body data.
|
||||
|
||||
@param n The number of bytes to commit. This must
|
||||
be less than or equal to the size of the buffer
|
||||
sequence returned by @ref prepare_body.
|
||||
|
||||
@note This member function is only available when
|
||||
`isDirect==true`.
|
||||
*/
|
||||
void
|
||||
commit_body(std::size_t n);
|
||||
|
||||
/** Indicate that body octets have been consumed.
|
||||
|
||||
@param n The number of bytes to consume.
|
||||
*/
|
||||
void
|
||||
consume(std::size_t n)
|
||||
{
|
||||
BOOST_ASSERT(n <= len_);
|
||||
BOOST_ASSERT(
|
||||
state_ == parse_state::body ||
|
||||
state_ == parse_state::chunk_body);
|
||||
len_ -= n;
|
||||
if(len_ == 0)
|
||||
{
|
||||
if(state_ == parse_state::body)
|
||||
state_ = parse_state::complete;
|
||||
else
|
||||
state_ = parse_state::chunk_header;
|
||||
}
|
||||
}
|
||||
put_eof(error_code& ec);
|
||||
|
||||
private:
|
||||
inline
|
||||
@@ -570,68 +434,42 @@ private:
|
||||
maybe_flatten(
|
||||
ConstBufferSequence const& buffers);
|
||||
|
||||
std::size_t
|
||||
do_write(boost::asio::const_buffers_1 const& buffer,
|
||||
void
|
||||
parse_header(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_header(char const*& p, char const* term,
|
||||
error_code& ec, std::true_type);
|
||||
|
||||
std::size_t
|
||||
do_write(boost::asio::const_buffers_1 const& buffer,
|
||||
void
|
||||
parse_header(char const*& p, char const* term,
|
||||
error_code& ec, std::false_type);
|
||||
|
||||
void
|
||||
parse_startline(char const*& it,
|
||||
int& version, int& status,
|
||||
error_code& ec, std::true_type);
|
||||
parse_body(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_startline(char const*& it,
|
||||
int& version, int& status,
|
||||
error_code& ec, std::false_type);
|
||||
parse_body_to_eof(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_fields(char const*& it,
|
||||
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
|
||||
parse_fields(char const*& p,
|
||||
char const* last, error_code& ec);
|
||||
|
||||
void
|
||||
do_field(
|
||||
string_view const& name,
|
||||
string_view const& value,
|
||||
error_code& ec);
|
||||
|
||||
std::size_t
|
||||
parse_header(char const* p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
do_header(int, std::true_type);
|
||||
|
||||
void
|
||||
do_header(int status, std::false_type);
|
||||
|
||||
void
|
||||
maybe_do_body_direct();
|
||||
|
||||
void
|
||||
maybe_do_body_indirect(error_code& ec);
|
||||
|
||||
std::size_t
|
||||
parse_chunk_header(char const* p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
std::size_t
|
||||
parse_body(char const* p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
std::size_t
|
||||
parse_body_to_eof(char const* p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
std::size_t
|
||||
parse_chunk_body(char const* p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
do_complete(error_code& ec);
|
||||
do_field(string_view const& name,
|
||||
string_view const& value,
|
||||
error_code& ec);
|
||||
};
|
||||
|
||||
} // http
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
#ifndef BEAST_HTTP_BUFFER_BODY_HPP
|
||||
#define BEAST_HTTP_BUFFER_BODY_HPP
|
||||
|
||||
#include <beast/http/type_traits.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/type_traits.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -31,41 +31,30 @@ namespace http {
|
||||
...
|
||||
}
|
||||
@endcode
|
||||
|
||||
@tparam isDeferred A `bool` which, when set to `true`,
|
||||
indicates to the serialization implementation that it should
|
||||
send the entire HTTP Header before attempting to acquire
|
||||
buffers representing the body.
|
||||
|
||||
@tparam ConstBufferSequence The type of buffer sequence
|
||||
stored in the body by the caller.
|
||||
*/
|
||||
template<
|
||||
bool isDeferred,
|
||||
class ConstBufferSequence>
|
||||
struct buffer_body
|
||||
{
|
||||
static_assert(is_const_buffer_sequence<ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
/// 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`.
|
||||
|
||||
/** The type of the body member when used in a message.
|
||||
If this is `nullptr` and @ref more is `true`, the error
|
||||
@ref error::need_buffer will be returned by a serializer
|
||||
when attempting to retrieve the next buffer.
|
||||
*/
|
||||
void* data;
|
||||
|
||||
When engaged, the first element of the pair represents
|
||||
the current buffer sequence to be written.
|
||||
/** The number of octets in the buffer pointed to by @ref data
|
||||
|
||||
The second element of the pair indicates whether or not
|
||||
additional buffers will be available. A value of `false`
|
||||
indicates the end of the message body.
|
||||
If @ref data is `nullptr` during serialization, this value
|
||||
is not inspected.
|
||||
*/
|
||||
std::size_t size;
|
||||
|
||||
If the buffer in the value is disengaged, and the second
|
||||
element of the pair is `true`, @ref serializer operations
|
||||
will return @ref http::error::need_more. This signals the
|
||||
calling code that a new buffer should be placed into the
|
||||
body, or that the caller should indicate that no more
|
||||
buffers will be available.
|
||||
*/
|
||||
using value_type =
|
||||
std::pair<boost::optional<ConstBufferSequence>, bool>;
|
||||
/// `true` if this is not the last buffer.
|
||||
bool more;
|
||||
};
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm to obtain buffers representing the body
|
||||
@@ -77,10 +66,10 @@ struct buffer_body
|
||||
value_type const& body_;
|
||||
|
||||
public:
|
||||
using is_deferred =
|
||||
std::integral_constant<bool, isDeferred>;
|
||||
using is_deferred = std::false_type;
|
||||
|
||||
using const_buffers_type = ConstBufferSequence;
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffers_1;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
@@ -95,44 +84,89 @@ struct buffer_body
|
||||
{
|
||||
}
|
||||
|
||||
boost::optional<std::pair<ConstBufferSequence, bool>>
|
||||
boost::optional<
|
||||
std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
if(toggle_)
|
||||
{
|
||||
if(body_.second)
|
||||
if(body_.more)
|
||||
{
|
||||
toggle_ = false;
|
||||
ec = error::need_more;
|
||||
ec = error::need_buffer;
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
if(body_.first)
|
||||
if(body_.data)
|
||||
{
|
||||
toggle_ = true;
|
||||
return {{*body_.first, body_.second}};
|
||||
return {{const_buffers_type{
|
||||
body_.data, body_.size}, body_.more}};
|
||||
}
|
||||
if(body_.second)
|
||||
ec = error::need_more;
|
||||
if(body_.more)
|
||||
ec = error::need_buffer;
|
||||
return boost::none;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm used store buffers in this body
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, buffer_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<std::uint64_t>, error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::buffer_copy;
|
||||
auto const n = buffer_size(buffers);
|
||||
if(! body_.data || n > body_.size)
|
||||
{
|
||||
ec = error::need_buffer;
|
||||
return;
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
buffer_copy(boost::asio::buffer(
|
||||
body_.data, body_.size), buffers);
|
||||
body_.data = reinterpret_cast<char*>(
|
||||
body_.data) + bytes_transferred;
|
||||
body_.size -= bytes_transferred;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
#if ! BEAST_DOXYGEN
|
||||
|
||||
// operator<< is not supported for buffer_body"
|
||||
template<bool isRequest, class Fields,
|
||||
bool isDeferred, class ConstBufferSequence>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, message<
|
||||
isRequest, buffer_body<isDeferred,
|
||||
ConstBufferSequence>, Fields> const& msg)
|
||||
{
|
||||
static_assert(sizeof(ConstBufferSequence) == -1,
|
||||
"operator<< is not supported for buffer_body");
|
||||
}
|
||||
|
||||
operator<<(std::ostream& os, message<isRequest,
|
||||
buffer_body, Fields> const& msg) = delete;
|
||||
#endif
|
||||
|
||||
} // http
|
||||
|
||||
@@ -62,6 +62,20 @@ namespace detail {
|
||||
class basic_parser_base
|
||||
{
|
||||
protected:
|
||||
enum class state
|
||||
{
|
||||
nothing_yet = 0,
|
||||
header,
|
||||
body0,
|
||||
body,
|
||||
body_to_eof0,
|
||||
body_to_eof,
|
||||
chunk_header0,
|
||||
chunk_header,
|
||||
chunk_body,
|
||||
complete
|
||||
};
|
||||
|
||||
static
|
||||
bool
|
||||
is_pathchar(char c)
|
||||
@@ -263,11 +277,9 @@ protected:
|
||||
bool
|
||||
parse_crlf(char const*& it)
|
||||
{
|
||||
if(*it != '\r')
|
||||
if( it[0] != '\r' && it[1] != '\n')
|
||||
return false;
|
||||
if(*++it != '\n')
|
||||
return false;
|
||||
++it;
|
||||
it += 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
#define BEAST_HTTP_DYNAMIC_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/multi_buffer.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <utility>
|
||||
@@ -28,57 +28,6 @@ struct basic_dynamic_body
|
||||
/// The type of the body member when used in a message.
|
||||
using value_type = DynamicBuffer;
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm used store buffers in this body
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
static bool constexpr is_direct = true;
|
||||
|
||||
using mutable_buffers_type =
|
||||
typename DynamicBuffer::mutable_buffers_type;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest,
|
||||
basic_dynamic_body, Fields>& msg)
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(std::uint64_t content_length)
|
||||
{
|
||||
}
|
||||
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n)
|
||||
{
|
||||
return body_.prepare(n);
|
||||
}
|
||||
|
||||
void
|
||||
commit(std::size_t n)
|
||||
{
|
||||
body_.commit(n);
|
||||
}
|
||||
|
||||
void
|
||||
finish()
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm to obtain buffers representing the body
|
||||
using reader = implementation_defined;
|
||||
@@ -119,6 +68,58 @@ struct basic_dynamic_body
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm used store buffers in this body
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest,
|
||||
basic_dynamic_body, Fields>& msg)
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> const&, error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> b;
|
||||
try
|
||||
{
|
||||
b.emplace(body_.prepare(
|
||||
buffer_size(buffers)));
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return;
|
||||
}
|
||||
body_.commit(buffer_copy(*b, buffers));
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
/** A dynamic message body represented by a @ref multi_buffer
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define BEAST_HTTP_EMPTY_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
@@ -26,6 +27,8 @@ struct empty_body
|
||||
/// The type of the body member when used in a message.
|
||||
struct value_type
|
||||
{
|
||||
// VFALCO We could stash boost::optional<std::uint64_t>
|
||||
// for the content length here, set on init()
|
||||
};
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
@@ -64,6 +67,39 @@ struct empty_body
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm used store buffers in this body
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
struct writer
|
||||
{
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, empty_body, Fields>& msg)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<std::uint64_t> const&,
|
||||
error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
put(ConstBufferSequence const&,
|
||||
error_code& ec)
|
||||
{
|
||||
ec = error::missing_body;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // http
|
||||
|
||||
@@ -14,36 +14,64 @@
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/// Error codes returned from HTTP parsing
|
||||
/// Error codes returned from HTTP algorithms and operations.
|
||||
enum class error
|
||||
{
|
||||
/** The end of the stream was reached.
|
||||
|
||||
This error is returned by @ref basic_parser::write_eof
|
||||
when the end of stream is reached and there are no
|
||||
unparsed bytes in the stream buffer.
|
||||
This error is returned under the following conditions:
|
||||
|
||||
@li When attempting to read HTTP data from a stream and the stream
|
||||
read returns the error `boost::asio::error::eof` before any new octets
|
||||
have been received.
|
||||
|
||||
@li When sending a complete HTTP message at once and the semantics of
|
||||
the message are that the connection should be closed to indicate the
|
||||
end of the message.
|
||||
*/
|
||||
end_of_stream = 1,
|
||||
|
||||
/** The incoming message is incomplete.
|
||||
|
||||
This happens when the end of stream is reached
|
||||
and some bytes have been received, but not the
|
||||
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 generated during serialization of HTTP
|
||||
messages using the @ref buffer_body representation.
|
||||
It indicates to the caller that an additional buffer
|
||||
sequence should be placed into the body, or that the
|
||||
caller should indicate that there are no more bytes
|
||||
remaining in the body to serialize.
|
||||
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 operaetion.
|
||||
*/
|
||||
need_more,
|
||||
|
||||
/** The message container has no body.
|
||||
|
||||
This error is returned when attempting to parse body
|
||||
octets into a message container which has the
|
||||
@ref empty_body body type.
|
||||
|
||||
@see @ref empty_body
|
||||
*/
|
||||
missing_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,
|
||||
|
||||
/** Buffer maximum exceeded.
|
||||
|
||||
This error is returned when reading HTTP content
|
||||
@@ -52,6 +80,10 @@ enum class error
|
||||
*/
|
||||
buffer_overflow,
|
||||
|
||||
//
|
||||
// (parser errors)
|
||||
//
|
||||
|
||||
/// The line ending was malformed
|
||||
bad_line_ending,
|
||||
|
||||
|
||||
@@ -33,18 +33,19 @@ namespace http {
|
||||
*/
|
||||
template<bool isRequest, class Fields>
|
||||
class header_parser
|
||||
: public basic_parser<isRequest, false,
|
||||
: public basic_parser<isRequest,
|
||||
header_parser<isRequest, Fields>>
|
||||
{
|
||||
header<isRequest, Fields> h_;
|
||||
string_view body_;
|
||||
|
||||
public:
|
||||
using mutable_buffers_type =
|
||||
boost::asio::null_buffers;
|
||||
|
||||
/// The type of @ref header this object produces.
|
||||
using value_type = header<isRequest, Fields>;
|
||||
|
||||
/// Default constructor.
|
||||
header_parser() = default;
|
||||
|
||||
/// Copy constructor.
|
||||
header_parser(header_parser const&) = default;
|
||||
|
||||
@@ -58,18 +59,49 @@ public:
|
||||
*/
|
||||
header_parser(header_parser&&) = default;
|
||||
|
||||
/** Move assignment
|
||||
|
||||
After the move, the only valid operation
|
||||
on the moved-from object is destruction.
|
||||
*/
|
||||
header_parser& operator=(header_parser&&) = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param args If present, additional arguments to be
|
||||
forwarded to the @ref beast::http::header constructor.
|
||||
@param args Optional arguments forwarded
|
||||
forwarded to the @ref http::header constructor.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class... Args>
|
||||
explicit
|
||||
header_parser(Args&&... args);
|
||||
#else
|
||||
template<class Arg0, class... ArgN,
|
||||
class = typename std::enable_if<
|
||||
! std::is_convertible<typename
|
||||
std::decay<Arg0>::type,
|
||||
header_parser>::value>>
|
||||
explicit
|
||||
header_parser(Arg0&& arg0, ArgN&&... argn);
|
||||
#endif
|
||||
|
||||
/** Returns parsed body octets.
|
||||
|
||||
This function will return the most recent buffer
|
||||
of octets corresponding to the parsed body. This
|
||||
buffer will become invalidated on any subsequent
|
||||
call to @ref put or @ref put_eof
|
||||
*/
|
||||
string_view
|
||||
body() const
|
||||
{
|
||||
return body_;
|
||||
}
|
||||
|
||||
/** Returns the parsed header
|
||||
|
||||
Only valid if @ref got_header would return `true`.
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
value_type const&
|
||||
get() const
|
||||
@@ -79,7 +111,8 @@ public:
|
||||
|
||||
/** Returns the parsed header.
|
||||
|
||||
Only valid if @ref got_header would return `true`.
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
value_type&
|
||||
get()
|
||||
@@ -89,8 +122,10 @@ public:
|
||||
|
||||
/** Returns ownership of the parsed header.
|
||||
|
||||
Ownership is transferred to the caller. Only
|
||||
valid if @ref got_header would return `true`.
|
||||
Ownership is transferred to the caller.
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
|
||||
Requires:
|
||||
@ref value_type is @b MoveConstructible
|
||||
@@ -98,20 +133,18 @@ public:
|
||||
value_type
|
||||
release()
|
||||
{
|
||||
static_assert(std::is_move_constructible<decltype(h_)>::value,
|
||||
static_assert(
|
||||
std::is_move_constructible<decltype(h_)>::value,
|
||||
"MoveConstructible requirements not met");
|
||||
return std::move(h_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parser<
|
||||
isRequest, false, header_parser>;
|
||||
friend class basic_parser<isRequest, header_parser>;
|
||||
|
||||
void
|
||||
on_request(
|
||||
string_view const& method,
|
||||
string_view const& path,
|
||||
int version, error_code&)
|
||||
on_request(string_view method,
|
||||
string_view path, int version, error_code&)
|
||||
{
|
||||
h_.target(path);
|
||||
h_.method(method);
|
||||
@@ -119,9 +152,8 @@ private:
|
||||
}
|
||||
|
||||
void
|
||||
on_response(int status,
|
||||
string_view const& reason,
|
||||
int version, error_code&)
|
||||
on_response(int status, string_view reason,
|
||||
int version, error_code&)
|
||||
{
|
||||
h_.status = status;
|
||||
h_.version = version;
|
||||
@@ -129,9 +161,8 @@ private:
|
||||
}
|
||||
|
||||
void
|
||||
on_field(string_view const& name,
|
||||
string_view const& value,
|
||||
error_code&)
|
||||
on_field(string_view name,
|
||||
string_view value, error_code&)
|
||||
{
|
||||
h_.fields.insert(name, value);
|
||||
}
|
||||
@@ -142,41 +173,28 @@ private:
|
||||
}
|
||||
|
||||
void
|
||||
on_body(error_code& ec)
|
||||
on_body(boost::optional<std::
|
||||
uint64_t> const&, error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_body(std::uint64_t content_length,
|
||||
error_code& ec)
|
||||
on_data(string_view s, error_code&)
|
||||
{
|
||||
body_ = s;
|
||||
}
|
||||
|
||||
void
|
||||
on_data(string_view const& s,
|
||||
error_code& ec)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_commit(std::size_t n)
|
||||
{
|
||||
// Can't write body data with header-only parser!
|
||||
BOOST_ASSERT(false);
|
||||
BOOST_THROW_EXCEPTION(std::logic_error{
|
||||
"invalid member function call"});
|
||||
}
|
||||
|
||||
void
|
||||
on_chunk(std::uint64_t n,
|
||||
string_view const& ext,
|
||||
error_code& ec)
|
||||
on_chunk(std::uint64_t,
|
||||
string_view const&, error_code&)
|
||||
{
|
||||
body_ = {};
|
||||
}
|
||||
|
||||
void
|
||||
on_complete(error_code&)
|
||||
{
|
||||
body_ = {};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,733 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_ASYNC_READ_IPP_HPP
|
||||
#define BEAST_HTTP_IMPL_ASYNC_READ_IPP_HPP
|
||||
|
||||
#include <beast/http/type_traits.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message_parser.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <beast/core/detail/read_size_helper.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
class Handler>
|
||||
class read_some_buffer_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
DynamicBuffer& db;
|
||||
basic_parser<isRequest, isDirect, Derived>& p;
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> mb;
|
||||
boost::optional<typename
|
||||
Derived::mutable_buffers_type> bb;
|
||||
std::size_t bytes_used;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, Stream& s_, DynamicBuffer& db_,
|
||||
basic_parser<isRequest, isDirect, Derived>& p_)
|
||||
: s(s_)
|
||||
, db(db_)
|
||||
, p(p_)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
cont = asio_handler_is_continuation(std::addressof(handler));
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
read_some_buffer_op(read_some_buffer_op&&) = default;
|
||||
read_some_buffer_op(read_some_buffer_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_some_buffer_op(DeducedHandler&& h,
|
||||
Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void*
|
||||
asio_handler_allocate(std::size_t size,
|
||||
read_some_buffer_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
void
|
||||
asio_handler_deallocate(
|
||||
void* p, std::size_t size,
|
||||
read_some_buffer_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool
|
||||
asio_handler_is_continuation(
|
||||
read_some_buffer_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void
|
||||
asio_handler_invoke(Function&& f,
|
||||
read_some_buffer_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
class Handler>
|
||||
void
|
||||
read_some_buffer_op<Stream, DynamicBuffer,
|
||||
isRequest, isDirect, Derived, Handler>::
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
if(d.state == 99)
|
||||
goto upcall;
|
||||
for(;;)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
if(d.db.size() == 0)
|
||||
{
|
||||
d.state = 2;
|
||||
break;
|
||||
}
|
||||
//[[fallthrough]]
|
||||
|
||||
case 1:
|
||||
{
|
||||
BOOST_ASSERT(d.db.size() > 0);
|
||||
d.bytes_used =
|
||||
d.p.write(d.db.data(), ec);
|
||||
if(d.bytes_used > 0 || ec)
|
||||
{
|
||||
// call handler
|
||||
if(d.state == 1)
|
||||
goto upcall;
|
||||
d.state = 99;
|
||||
d.s.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
return;
|
||||
}
|
||||
//[[fallthrough]]
|
||||
}
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
{
|
||||
using beast::detail::read_size_helper;
|
||||
auto const size =
|
||||
read_size_helper(d.db, 65536);
|
||||
BOOST_ASSERT(size > 0);
|
||||
try
|
||||
{
|
||||
d.mb.emplace(d.db.prepare(size));
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
// call handler
|
||||
if(d.state == 3)
|
||||
goto upcall;
|
||||
d.state = 99;
|
||||
d.s.get_io_service().post(
|
||||
bind_handler(std::move(*this),
|
||||
error::buffer_overflow, 0));
|
||||
return;
|
||||
}
|
||||
// read
|
||||
d.state = 4;
|
||||
d.s.async_read_some(*d.mb, std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
case 4:
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
d.bytes_used = 0;
|
||||
if(! d.p.got_some())
|
||||
{
|
||||
ec = error::end_of_stream;
|
||||
goto upcall;
|
||||
}
|
||||
// caller sees end_of_stream on next read.
|
||||
ec = {};
|
||||
d.p.write_eof(ec);
|
||||
if(ec)
|
||||
goto upcall;
|
||||
BOOST_ASSERT(d.p.is_complete());
|
||||
goto upcall;
|
||||
}
|
||||
if(ec)
|
||||
{
|
||||
d.bytes_used = 0;
|
||||
goto upcall;
|
||||
}
|
||||
BOOST_ASSERT(bytes_transferred > 0);
|
||||
d.db.commit(bytes_transferred);
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
upcall:
|
||||
// can't pass any members of `d` otherwise UB
|
||||
auto const bytes_used = d.bytes_used;
|
||||
d_.invoke(ec, bytes_used);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Handler>
|
||||
class read_some_body_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
DynamicBuffer& db;
|
||||
basic_parser<isRequest, true, Derived>& p;
|
||||
boost::optional<typename
|
||||
Derived::mutable_buffers_type> mb;
|
||||
std::size_t bytes_used;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, Stream& s_, DynamicBuffer& db_,
|
||||
basic_parser<isRequest, true, Derived>& p_)
|
||||
: s(s_)
|
||||
, db(db_)
|
||||
, p(p_)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
cont = asio_handler_is_continuation(std::addressof(handler));
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
read_some_body_op(read_some_body_op&&) = default;
|
||||
read_some_body_op(read_some_body_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_some_body_op(DeducedHandler&& h,
|
||||
Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void*
|
||||
asio_handler_allocate(std::size_t size,
|
||||
read_some_body_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
void
|
||||
asio_handler_deallocate(
|
||||
void* p, std::size_t size,
|
||||
read_some_body_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool
|
||||
asio_handler_is_continuation(
|
||||
read_some_body_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void
|
||||
asio_handler_invoke(Function&& f,
|
||||
read_some_body_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Handler>
|
||||
void
|
||||
read_some_body_op<Stream, DynamicBuffer,
|
||||
isRequest, Derived, Handler>::
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
if(d.state == 99)
|
||||
goto upcall;
|
||||
for(;;)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
if(d.db.size() > 0)
|
||||
{
|
||||
d.bytes_used = d.p.copy_body(d.db);
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.s.get_io_service().post(
|
||||
bind_handler(std::move(*this),
|
||||
ec, 0));
|
||||
return;
|
||||
}
|
||||
d.p.prepare_body(d.mb, 65536);
|
||||
// read
|
||||
d.state = 1;
|
||||
d.s.async_read_some(
|
||||
*d.mb, std::move(*this));
|
||||
return;
|
||||
|
||||
case 1:
|
||||
d.bytes_used = 0;
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
// caller sees EOF on next read
|
||||
ec = {};
|
||||
d.p.write_eof(ec);
|
||||
if(ec)
|
||||
goto upcall;
|
||||
BOOST_ASSERT(d.p.is_complete());
|
||||
}
|
||||
else if(! ec)
|
||||
{
|
||||
d.p.commit_body(bytes_transferred);
|
||||
}
|
||||
goto upcall;
|
||||
}
|
||||
}
|
||||
upcall:
|
||||
// can't pass any members of `d` otherwise UB
|
||||
auto const bytes_used = d.bytes_used;
|
||||
d_.invoke(ec, bytes_used);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
class Handler>
|
||||
class parse_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
DynamicBuffer& db;
|
||||
basic_parser<isRequest, isDirect, Derived>& p;
|
||||
|
||||
data(Handler& handler, Stream& s_, DynamicBuffer& db_,
|
||||
basic_parser<isRequest, isDirect, Derived>& p_)
|
||||
: s(s_)
|
||||
, db(db_)
|
||||
, p(p_)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
cont = asio_handler_is_continuation(std::addressof(handler));
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
parse_op(parse_op&&) = default;
|
||||
parse_op(parse_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
parse_op(DeducedHandler&& h,
|
||||
Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code const& ec,
|
||||
std::size_t bytes_used, bool again = true);
|
||||
|
||||
friend
|
||||
void*
|
||||
asio_handler_allocate(
|
||||
std::size_t size, parse_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
void
|
||||
asio_handler_deallocate(
|
||||
void* p, std::size_t size,
|
||||
parse_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool
|
||||
asio_handler_is_continuation(
|
||||
parse_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void
|
||||
asio_handler_invoke(
|
||||
Function&& f, parse_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
class Handler>
|
||||
void
|
||||
parse_op<Stream, DynamicBuffer,
|
||||
isRequest, isDirect, Derived, Handler>::
|
||||
operator()(error_code const& ec,
|
||||
std::size_t bytes_used, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
if(! ec)
|
||||
{
|
||||
d.db.consume(bytes_used);
|
||||
if(! d.p.is_complete())
|
||||
return async_read_some(
|
||||
d.s, d.db, d.p, std::move(*this));
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Handler>
|
||||
class read_message_op
|
||||
{
|
||||
using parser_type =
|
||||
message_parser<isRequest, Body, Fields>;
|
||||
|
||||
using message_type =
|
||||
message<isRequest, Body, Fields>;
|
||||
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
DynamicBuffer& db;
|
||||
message_type& m;
|
||||
parser_type p;
|
||||
bool started = false;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, Stream& s_,
|
||||
DynamicBuffer& sb_, message_type& m_)
|
||||
: s(s_)
|
||||
, db(sb_)
|
||||
, m(m_)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
cont = asio_handler_is_continuation(std::addressof(handler));
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
read_message_op(read_message_op&&) = default;
|
||||
read_message_op(read_message_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_message_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_message_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_message_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_message_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_message_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Handler>
|
||||
void
|
||||
read_message_op<Stream, DynamicBuffer, isRequest, Body, Fields, Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
if(ec)
|
||||
goto upcall;
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
beast::http::async_read(
|
||||
d.s, d.db, d.p, std::move(*this));
|
||||
return;
|
||||
|
||||
case 1:
|
||||
d.m = d.p.release();
|
||||
goto upcall;
|
||||
}
|
||||
upcall:
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code, std::size_t)>
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, true, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
async_completion<ReadHandler,
|
||||
void(error_code, std::size_t)> init{handler};
|
||||
switch(parser.state())
|
||||
{
|
||||
case parse_state::header:
|
||||
case parse_state::chunk_header:
|
||||
detail::read_some_buffer_op<AsyncReadStream,
|
||||
DynamicBuffer, isRequest, true, Derived, handler_type<
|
||||
ReadHandler, void(error_code, std::size_t)>>{
|
||||
init.completion_handler, stream, buffer, parser};
|
||||
break;
|
||||
|
||||
default:
|
||||
detail::read_some_body_op<AsyncReadStream,
|
||||
DynamicBuffer, isRequest, Derived, handler_type<
|
||||
ReadHandler, void(error_code, std::size_t)>>{
|
||||
init.completion_handler, stream, buffer, parser};
|
||||
break;
|
||||
}
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
inline
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code, std::size_t)>
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, false, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
async_completion<ReadHandler,
|
||||
void(error_code, std::size_t)> init{handler};
|
||||
detail::read_some_buffer_op<AsyncReadStream,
|
||||
DynamicBuffer, isRequest, false, Derived, handler_type<
|
||||
ReadHandler, void(error_code, std::size_t)>>{
|
||||
init.completion_handler, stream, buffer, parser};
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code, std::size_t)>
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
return detail::async_read_some(stream, buffer, parser,
|
||||
std::forward<ReadHandler>(handler));
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code)>
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::parse_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, isDirect, Derived, handler_type<
|
||||
ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, parser};
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code)>
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::read_message_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Body, Fields, handler_type<
|
||||
ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, msg};
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,10 +12,11 @@ namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
template<class... Args>
|
||||
template<class Arg0, class... ArgN, class>
|
||||
header_parser<isRequest, Fields>::
|
||||
header_parser(Args&&... args)
|
||||
: h_(std::forward<Args>(args)...)
|
||||
header_parser(Arg0&& arg0, ArgN&&... argn)
|
||||
: h_(std::forward<Arg0>(arg0),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,7 @@ message_parser<isRequest, Body, Fields>::
|
||||
message_parser(header_parser<
|
||||
isRequest, Fields>&& parser, Args&&... args)
|
||||
: base_type(std::move(static_cast<basic_parser<
|
||||
isRequest, false, header_parser<
|
||||
isRequest, Fields>>&>(parser)))
|
||||
isRequest, header_parser<isRequest, Fields>>&>(parser)))
|
||||
, m_(parser.release(), std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
+595
-144
@@ -28,159 +28,396 @@ namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
inline
|
||||
std::size_t
|
||||
read_some_buffer(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
error_code& ec)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Handler>
|
||||
class read_some_op
|
||||
{
|
||||
std::size_t bytes_used;
|
||||
if(buffer.size() == 0)
|
||||
goto do_read;
|
||||
for(;;)
|
||||
int state_ = 0;
|
||||
std::size_t used_ = 0;
|
||||
Stream& s_;
|
||||
DynamicBuffer& b_;
|
||||
basic_parser<isRequest, Derived>& p_;
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> mb_;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
read_some_op(read_some_op&&) = default;
|
||||
read_some_op(read_some_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
read_some_op(DeducedHandler&& h, Stream& s,
|
||||
DynamicBuffer& b, basic_parser<isRequest, Derived>& p)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, p_(p)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
bytes_used = parser.write(
|
||||
buffer.data(), ec);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, std::size_t bytes_transferred);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->state_ >= 2 ? true :
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Handler>
|
||||
void
|
||||
read_some_op<Stream, DynamicBuffer,
|
||||
isRequest, Derived, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
switch(state_)
|
||||
{
|
||||
case 0:
|
||||
state_ = 1;
|
||||
if(b_.size() == 0)
|
||||
goto do_read;
|
||||
goto do_parse;
|
||||
|
||||
case 1:
|
||||
state_ = 2;
|
||||
case 2:
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
if(p_.got_some())
|
||||
{
|
||||
// caller sees EOF on next read
|
||||
ec = {};
|
||||
p_.put_eof(ec);
|
||||
if(ec)
|
||||
goto upcall;
|
||||
BOOST_ASSERT(p_.is_done());
|
||||
goto upcall;
|
||||
}
|
||||
ec = error::end_of_stream;
|
||||
goto upcall;
|
||||
}
|
||||
if(ec)
|
||||
return 0;
|
||||
if(bytes_used > 0)
|
||||
goto do_finish;
|
||||
goto upcall;
|
||||
b_.commit(bytes_transferred);
|
||||
|
||||
do_parse:
|
||||
used_ += p_.put(b_.data(), ec);
|
||||
if(! ec || ec != http::error::need_more)
|
||||
goto do_upcall;
|
||||
ec = {};
|
||||
// [[fallthrough]]
|
||||
|
||||
do_read:
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> mb;
|
||||
using beast::detail::read_size_helper;
|
||||
auto const size =
|
||||
read_size_helper(buffer, 65536);
|
||||
BOOST_ASSERT(size > 0);
|
||||
try
|
||||
{
|
||||
mb.emplace(buffer.prepare(size));
|
||||
using beast::detail::read_size_helper;
|
||||
mb_.emplace(b_.prepare(
|
||||
read_size_helper(b_, 65536)));
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return 0;
|
||||
goto do_upcall;
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
stream.read_some(*mb, ec);
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
bytes_used = 0;
|
||||
if(parser.got_some())
|
||||
{
|
||||
// caller sees EOF on next read
|
||||
ec = {};
|
||||
parser.write_eof(ec);
|
||||
if(ec)
|
||||
return 0;
|
||||
BOOST_ASSERT(parser.is_complete());
|
||||
break;
|
||||
}
|
||||
ec = error::end_of_stream;
|
||||
break;
|
||||
}
|
||||
else if(ec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
BOOST_ASSERT(bytes_transferred > 0);
|
||||
buffer.commit(bytes_transferred);
|
||||
return s_.async_read_some(*mb_, std::move(*this));
|
||||
|
||||
do_upcall:
|
||||
if(state_ >= 2)
|
||||
goto upcall;
|
||||
state_ = 3;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
|
||||
case 3:
|
||||
break;
|
||||
}
|
||||
do_finish:
|
||||
return bytes_used;
|
||||
upcall:
|
||||
h_(ec, used_);
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
inline
|
||||
std::size_t
|
||||
read_some_body(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, true, Derived>& parser,
|
||||
error_code& ec)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct parser_is_done
|
||||
{
|
||||
if(buffer.size() > 0)
|
||||
return parser.copy_body(buffer);
|
||||
boost::optional<typename
|
||||
Derived::mutable_buffers_type> mb;
|
||||
try
|
||||
template<bool isRequest, class Derived>
|
||||
bool
|
||||
operator()(basic_parser<
|
||||
isRequest, Derived> const& p) const
|
||||
{
|
||||
parser.prepare_body(mb, 65536);
|
||||
return p.is_done();
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
};
|
||||
|
||||
struct parser_is_header_done
|
||||
{
|
||||
template<bool isRequest, class Derived>
|
||||
bool
|
||||
operator()(basic_parser<
|
||||
isRequest, Derived> const& p) const
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return 0;
|
||||
return p.is_header_done();
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
stream.read_some(*mb, ec);
|
||||
if(ec == boost::asio::error::eof)
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Condition,
|
||||
class Handler>
|
||||
class read_op
|
||||
{
|
||||
int state_ = 0;
|
||||
Stream& s_;
|
||||
DynamicBuffer& b_;
|
||||
basic_parser<isRequest, Derived>& p_;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
read_op(read_op&&) = default;
|
||||
read_op(read_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
read_op(DeducedHandler&& h, Stream& s,
|
||||
DynamicBuffer& b, basic_parser<isRequest,
|
||||
Derived>& p)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, p_(p)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
// caller sees EOF on next read
|
||||
ec = {};
|
||||
parser.write_eof(ec);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, std::size_t bytes_used);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->state_ >= 3 ? true :
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Condition,
|
||||
class Handler>
|
||||
void
|
||||
read_op<Stream, DynamicBuffer,
|
||||
isRequest, Derived, Condition, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_used)
|
||||
{
|
||||
switch(state_)
|
||||
{
|
||||
case 0:
|
||||
if(Condition{}(p_))
|
||||
{
|
||||
state_ = 1;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
state_ = 2;
|
||||
// [[fallthrough]]
|
||||
|
||||
do_read:
|
||||
return async_read_some(
|
||||
s_, b_, p_, std::move(*this));
|
||||
|
||||
case 1:
|
||||
goto upcall;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
b_.consume(bytes_used);
|
||||
if(ec)
|
||||
return 0;
|
||||
BOOST_ASSERT(parser.is_complete());
|
||||
goto upcall;
|
||||
if(Condition{}(p_))
|
||||
goto upcall;
|
||||
state_ = 3;
|
||||
goto do_read;
|
||||
}
|
||||
else if(! ec)
|
||||
{
|
||||
parser.commit_body(bytes_transferred);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
upcall:
|
||||
h_(ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
inline
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, true, Derived>& parser,
|
||||
error_code& ec)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Handler>
|
||||
class read_msg_op
|
||||
{
|
||||
switch(parser.state())
|
||||
using parser_type =
|
||||
message_parser<isRequest, Body, Fields>;
|
||||
|
||||
using message_type =
|
||||
message<isRequest, Body, Fields>;
|
||||
|
||||
struct data
|
||||
{
|
||||
case parse_state::header:
|
||||
case parse_state::chunk_header:
|
||||
return detail::read_some_buffer(
|
||||
stream, buffer, parser, ec);
|
||||
int state = 0;
|
||||
Stream& s;
|
||||
DynamicBuffer& b;
|
||||
message_type& m;
|
||||
parser_type p;
|
||||
|
||||
default:
|
||||
return detail::read_some_body(
|
||||
stream, buffer, parser, ec);
|
||||
data(Handler& handler, Stream& s_,
|
||||
DynamicBuffer& b_, message_type& m_)
|
||||
: s(s_)
|
||||
, b(b_)
|
||||
, m(m_)
|
||||
{
|
||||
p.eager(true);
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
read_msg_op(read_msg_op&&) = default;
|
||||
read_msg_op(read_msg_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
inline
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, false, Derived>& parser,
|
||||
error_code& ec)
|
||||
void
|
||||
operator()(error_code ec, std::size_t bytes_used);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->d_->state >= 2 ? true :
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Handler>
|
||||
void
|
||||
read_msg_op<Stream, DynamicBuffer,
|
||||
isRequest, Body, Fields, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_used)
|
||||
{
|
||||
return detail::read_some_buffer(
|
||||
stream, buffer, parser, ec);
|
||||
auto& d = *d_;
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
|
||||
do_read:
|
||||
return async_read_some(
|
||||
d.s, d.b, d.p, std::move(*this));
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
d.b.consume(bytes_used);
|
||||
if(ec)
|
||||
goto upcall;
|
||||
if(d.p.is_done())
|
||||
{
|
||||
d.m = d.p.release();
|
||||
goto upcall;
|
||||
}
|
||||
d.state = 2;
|
||||
goto do_read;
|
||||
}
|
||||
upcall:
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
@@ -190,18 +427,18 @@ read_some(
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser)
|
||||
basic_parser<isRequest, Derived>& parser)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
BOOST_ASSERT(! parser.is_done());
|
||||
error_code ec;
|
||||
auto const bytes_used =
|
||||
read_some(stream, buffer, parser, ec);
|
||||
@@ -213,37 +450,190 @@ read_some(
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
return detail::read_some(stream, buffer, parser, ec);
|
||||
BOOST_ASSERT(! parser.is_done());
|
||||
std::size_t bytes_used = 0;
|
||||
if(buffer.size() == 0)
|
||||
goto do_read;
|
||||
for(;;)
|
||||
{
|
||||
// invoke parser
|
||||
bytes_used += parser.put(buffer.data(), ec);
|
||||
if(! ec)
|
||||
break;
|
||||
if(ec != http::error::need_more)
|
||||
break;
|
||||
ec = {};
|
||||
do_read:
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> b;
|
||||
try
|
||||
{
|
||||
using beast::detail::read_size_helper;
|
||||
b.emplace(buffer.prepare(
|
||||
read_size_helper(buffer, 65536)));
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
break;
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
stream.read_some(*b, ec);
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
if(parser.got_some())
|
||||
{
|
||||
// caller sees EOF on next read
|
||||
ec = {};
|
||||
parser.put_eof(ec);
|
||||
if(ec)
|
||||
break;
|
||||
BOOST_ASSERT(parser.is_done());
|
||||
break;
|
||||
}
|
||||
ec = error::end_of_stream;
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
break;
|
||||
buffer.commit(bytes_transferred);
|
||||
}
|
||||
return bytes_used;
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code, std::size_t)>
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_done());
|
||||
async_completion<ReadHandler,
|
||||
void(error_code, std::size_t)> init{handler};
|
||||
detail::read_some_op<AsyncReadStream,
|
||||
DynamicBuffer, isRequest, Derived, handler_type<
|
||||
ReadHandler, void(error_code, std::size_t)>>{
|
||||
init.completion_handler, stream, buffer, parser}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser)
|
||||
basic_parser<isRequest, Derived>& parser)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
error_code ec;
|
||||
read_header(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(false);
|
||||
while(! parser.is_header_done())
|
||||
{
|
||||
auto const bytes_used =
|
||||
read_some(stream, buffer, parser, ec);
|
||||
buffer.consume(bytes_used);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code)>
|
||||
async_read_header(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(false);
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::read_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Derived, detail::parser_is_header_done,
|
||||
handler_type<ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, parser}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
error_code ec;
|
||||
read(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
@@ -253,30 +643,59 @@ read(
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
do
|
||||
parser.eager(true);
|
||||
while(! parser.is_done())
|
||||
{
|
||||
auto const bytes_used =
|
||||
read_some(stream, buffer, parser, ec);
|
||||
auto const bytes_used = read_some(
|
||||
stream, buffer, parser, ec);
|
||||
buffer.consume(bytes_used);
|
||||
if(ec)
|
||||
return;
|
||||
buffer.consume(bytes_used);
|
||||
}
|
||||
while(! parser.is_complete());
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code)>
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(true);
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::read_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Derived, detail::parser_is_done,
|
||||
handler_type<ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, parser}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
@@ -296,7 +715,7 @@ read(
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
error_code ec;
|
||||
beast::http::read(stream, buffer, msg, ec);
|
||||
read(stream, buffer, msg, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
@@ -321,12 +740,44 @@ read(
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
message_parser<isRequest, Body, Fields> p;
|
||||
beast::http::read(stream, buffer, p, ec);
|
||||
p.eager(true);
|
||||
read(stream, buffer, p.base(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
msg = p.release();
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code)>
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::read_msg_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Body, Fields, handler_type<
|
||||
ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, msg}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
|
||||
@@ -81,19 +81,7 @@ serializer(message<isRequest, Body, Fields> const& m,
|
||||
: m_(m)
|
||||
, d_(d)
|
||||
, b_(1024, alloc)
|
||||
, chunked_(token_list{
|
||||
m.fields["Transfer-Encoding"]}.exists("chunked"))
|
||||
, close_(token_list{
|
||||
m.fields["Connection"]}.exists("close") ||
|
||||
(m.version < 11 && ! m.fields.exists(
|
||||
"Content-Length")))
|
||||
{
|
||||
s_ = chunked_ ? do_init_c : do_init;
|
||||
// VFALCO Move this stuff to the switch?
|
||||
auto os = ostream(b_);
|
||||
detail::write_start_line(os, m_);
|
||||
detail::write_fields(os, m_.fields);
|
||||
os << "\r\n";
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields,
|
||||
@@ -107,6 +95,22 @@ get(error_code& ec, Visit&& visit)
|
||||
using boost::asio::buffer_size;
|
||||
switch(s_)
|
||||
{
|
||||
case do_construct:
|
||||
{
|
||||
chunked_ = token_list{
|
||||
m_.fields["Transfer-Encoding"]}.exists("chunked");
|
||||
close_ = token_list{m_.fields["Connection"]}.exists("close") ||
|
||||
(m_.version < 11 && ! m_.fields.exists("Content-Length"));
|
||||
auto os = ostream(b_);
|
||||
detail::write_start_line(os, m_);
|
||||
detail::write_fields(os, m_.fields);
|
||||
os << "\r\n";
|
||||
if(chunked_)
|
||||
goto go_init_c;
|
||||
s_ = do_init;
|
||||
// [[fallthrough]]
|
||||
}
|
||||
|
||||
case do_init:
|
||||
{
|
||||
if(split_)
|
||||
@@ -171,6 +175,8 @@ get(error_code& ec, Visit&& visit)
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
go_init_c:
|
||||
s_ = do_init_c;
|
||||
case do_init_c:
|
||||
{
|
||||
if(split_)
|
||||
@@ -320,7 +326,7 @@ consume(std::size_t n)
|
||||
break;
|
||||
// VFALCO delete b_?
|
||||
header_done_ = true;
|
||||
if(! is_deferred::value)
|
||||
if(! split_)
|
||||
goto go_complete;
|
||||
s_ = do_body;
|
||||
break;
|
||||
@@ -364,7 +370,7 @@ consume(std::size_t n)
|
||||
break;
|
||||
// VFALCO delete b_?
|
||||
header_done_ = true;
|
||||
if(! is_deferred::value)
|
||||
if(! split_)
|
||||
{
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <ostream>
|
||||
@@ -27,12 +28,14 @@ namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<
|
||||
class Stream, class Serializer, class Handler>
|
||||
template<class Stream, bool isRequest, class Body,
|
||||
class Fields, class Decorator, class Allocator,
|
||||
class Handler>
|
||||
class write_some_op
|
||||
{
|
||||
Stream& s_;
|
||||
Serializer& sr_;
|
||||
serializer<isRequest, Body, Fields,
|
||||
Decorator, Allocator>& sr_;
|
||||
Handler h_;
|
||||
|
||||
class lambda
|
||||
@@ -40,7 +43,7 @@ class write_some_op
|
||||
write_some_op& op_;
|
||||
|
||||
public:
|
||||
bool empty = true;
|
||||
bool invoked = false;
|
||||
|
||||
explicit
|
||||
lambda(write_some_op& op)
|
||||
@@ -53,7 +56,7 @@ class write_some_op
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffer)
|
||||
{
|
||||
empty = false;
|
||||
invoked = true;
|
||||
return op_.s_.async_write_some(
|
||||
buffer, std::move(op_));
|
||||
}
|
||||
@@ -65,7 +68,8 @@ public:
|
||||
|
||||
template<class DeducedHandler>
|
||||
write_some_op(DeducedHandler&& h,
|
||||
Stream& s, Serializer& sr)
|
||||
Stream& s, serializer<isRequest, Body,
|
||||
Fields, Decorator, Allocator>& sr)
|
||||
: s_(s)
|
||||
, sr_(sr)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
@@ -114,25 +118,40 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template<
|
||||
class Stream, class Serializer, class Handler>
|
||||
template<class Stream, bool isRequest, class Body,
|
||||
class Fields, class Decorator, class Allocator,
|
||||
class Handler>
|
||||
void
|
||||
write_some_op<Stream, Serializer, Handler>::
|
||||
write_some_op<Stream, isRequest, Body,
|
||||
Fields, Decorator, Allocator, Handler>::
|
||||
operator()()
|
||||
{
|
||||
lambda f{*this};
|
||||
error_code ec;
|
||||
if(sr_.is_done())
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
lambda f{*this};
|
||||
sr_.get(ec, f);
|
||||
if(! ec && ! f.empty)
|
||||
if(ec)
|
||||
{
|
||||
BOOST_ASSERT(! f.invoked);
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
if(f.invoked)
|
||||
// *this has been moved from,
|
||||
// cannot access members here.
|
||||
return;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
|
||||
template<
|
||||
class Stream, class Serializer, class Handler>
|
||||
template<class Stream, bool isRequest, class Body,
|
||||
class Fields, class Decorator, class Allocator,
|
||||
class Handler>
|
||||
void
|
||||
write_some_op<Stream, Serializer, Handler>::
|
||||
write_some_op<Stream, isRequest, Body,
|
||||
Fields, Decorator, Allocator, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
if(! ec)
|
||||
@@ -142,13 +161,199 @@ operator()(error_code ec, std::size_t bytes_transferred)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct serializer_is_header_done
|
||||
{
|
||||
template<bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
bool
|
||||
operator()(serializer<isRequest, Body, Fields,
|
||||
Decorator, Allocator> const& sr) const
|
||||
{
|
||||
return sr.is_header_done();
|
||||
}
|
||||
};
|
||||
|
||||
struct serializer_is_done
|
||||
{
|
||||
template<bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
bool
|
||||
operator()(serializer<isRequest, Body, Fields,
|
||||
Decorator, Allocator> const& sr) const
|
||||
{
|
||||
return sr.is_done();
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, bool isRequest, class Body,
|
||||
class Fields, class Decorator, class Allocator,
|
||||
class Predicate, class Handler>
|
||||
class write_op
|
||||
{
|
||||
int state_ = 0;
|
||||
Stream& s_;
|
||||
serializer<isRequest, Body, Fields,
|
||||
Decorator, Allocator>& sr_;
|
||||
Handler h_;
|
||||
|
||||
class lambda
|
||||
{
|
||||
write_op& op_;
|
||||
|
||||
public:
|
||||
bool invoked = false;
|
||||
|
||||
explicit
|
||||
lambda(write_op& op)
|
||||
: op_(op)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffer)
|
||||
{
|
||||
invoked = true;
|
||||
return op_.s_.async_write_some(
|
||||
buffer, std::move(op_));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
write_op(DeducedHandler&& h, Stream& s,
|
||||
serializer<isRequest, Body, Fields,
|
||||
Decorator, Allocator>& sr)
|
||||
: s_(s)
|
||||
, sr_(sr)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->state_ >= 3 ||
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, bool isRequest, class Body,
|
||||
class Fields, class Decorator, class Allocator,
|
||||
class Predicate, class Handler>
|
||||
void
|
||||
write_op<Stream, isRequest, Body, Fields,
|
||||
Decorator, Allocator, Predicate, Handler>::
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
if(ec)
|
||||
goto upcall;
|
||||
switch(state_)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if(Predicate{}(sr_))
|
||||
{
|
||||
state_ = 1;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
lambda f{*this};
|
||||
state_ = 2;
|
||||
sr_.get(ec, f);
|
||||
if(ec)
|
||||
{
|
||||
BOOST_ASSERT(! f.invoked);
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
if(f.invoked)
|
||||
// *this has been moved from,
|
||||
// cannot access members here.
|
||||
return;
|
||||
BOOST_ASSERT(Predicate{}(sr_));
|
||||
state_ = 1;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
|
||||
case 1:
|
||||
goto upcall;
|
||||
|
||||
case 2:
|
||||
state_ = 3;
|
||||
// [[fallthrough]]
|
||||
|
||||
case 3:
|
||||
{
|
||||
sr_.consume(bytes_transferred);
|
||||
if(Predicate{}(sr_))
|
||||
goto upcall;
|
||||
lambda f{*this};
|
||||
sr_.get(ec, f);
|
||||
if(ec)
|
||||
{
|
||||
BOOST_ASSERT(! f.invoked);
|
||||
goto upcall;
|
||||
}
|
||||
if(f.invoked)
|
||||
// *this has been moved from,
|
||||
// cannot access members here.
|
||||
return;
|
||||
BOOST_ASSERT(Predicate{}(sr_));
|
||||
goto upcall;
|
||||
}
|
||||
}
|
||||
upcall:
|
||||
h_(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_op
|
||||
class write_msg_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
int state = 0;
|
||||
Stream& s;
|
||||
serializer<isRequest, Body, Fields,
|
||||
empty_decorator, handler_alloc<char, Handler>> sr;
|
||||
@@ -165,22 +370,25 @@ class write_op
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
write_msg_op(write_msg_op&&) = default;
|
||||
write_msg_op(write_msg_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
write_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()();
|
||||
|
||||
void
|
||||
operator()(error_code ec);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_op* op)
|
||||
std::size_t size, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
@@ -189,7 +397,7 @@ public:
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_op* op)
|
||||
void* p, std::size_t size, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
@@ -197,17 +405,16 @@ public:
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_op* op)
|
||||
bool asio_handler_is_continuation(write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->d_->state == 2 ||
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->d_.handler()));
|
||||
return asio_handler_is_continuation(
|
||||
std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
void asio_handler_invoke(Function&& f, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
@@ -218,35 +425,23 @@ public:
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_op<Stream, Handler, isRequest, Body, Fields>::
|
||||
write_msg_op<Stream, Handler, isRequest, Body, Fields>::
|
||||
operator()()
|
||||
{
|
||||
auto& d = *d_;
|
||||
return async_write(d.s, d.sr, std::move(*this));
|
||||
}
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_msg_op<Stream, Handler, isRequest, Body, Fields>::
|
||||
operator()(error_code ec)
|
||||
{
|
||||
auto& d = *d_;
|
||||
if(ec)
|
||||
goto upcall;
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
write_some_op<Stream, decltype(d.sr),
|
||||
write_op>{std::move(*this), d.s, d.sr}();
|
||||
return;
|
||||
case 1:
|
||||
d.state = 2;
|
||||
// [[fallthrough]]
|
||||
case 2:
|
||||
if(d.sr.is_done())
|
||||
{
|
||||
if(d.sr.needs_close())
|
||||
// VFALCO Choose an error code
|
||||
ec = boost::asio::error::eof;
|
||||
break;
|
||||
}
|
||||
write_some_op<Stream, decltype(d.sr),
|
||||
write_op>{std::move(*this), d.s, d.sr}();
|
||||
return;
|
||||
}
|
||||
upcall:
|
||||
if(! ec)
|
||||
if(d.sr.need_close())
|
||||
ec = error::end_of_stream;
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
@@ -258,7 +453,8 @@ class write_some_lambda
|
||||
Stream& stream_;
|
||||
|
||||
public:
|
||||
boost::optional<std::size_t> bytes_transferred;
|
||||
bool invoked = false;
|
||||
std::size_t bytes_transferred = 0;
|
||||
|
||||
explicit
|
||||
write_some_lambda(Stream& stream)
|
||||
@@ -271,25 +467,55 @@ public:
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffer)
|
||||
{
|
||||
invoked = true;
|
||||
bytes_transferred =
|
||||
stream_.write_some(buffer, 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& buffer)
|
||||
{
|
||||
invoked = true;
|
||||
bytes_transferred = boost::asio::write(
|
||||
stream_, buffer, ec);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
template<class SyncWriteStream, bool isRequest,
|
||||
class Body, class Fields, class Decorator,
|
||||
class Allocator>
|
||||
void
|
||||
write_some(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
error_code ec;
|
||||
write_some(stream, sr, ec);
|
||||
if(ec)
|
||||
@@ -307,15 +533,18 @@ write_some(SyncWriteStream& stream, serializer<
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
detail::write_some_lambda<SyncWriteStream> f{stream};
|
||||
sr.get(ec, f);
|
||||
if(! ec)
|
||||
if(! sr.is_done())
|
||||
{
|
||||
if(f.bytes_transferred)
|
||||
sr.consume(*f.bytes_transferred);
|
||||
if(sr.is_done() && sr.needs_close())
|
||||
// VFALCO decide on an error code
|
||||
ec = boost::asio::error::eof;
|
||||
sr.get(ec, f);
|
||||
if(ec)
|
||||
return;
|
||||
if(f.invoked)
|
||||
sr.consume(f.bytes_transferred);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,12 +565,159 @@ async_write_some(AsyncWriteStream& stream, serializer<
|
||||
"BodyReader requirements not met");
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_some_op<AsyncWriteStream, decltype(sr),
|
||||
handler_type<WriteHandler, void(error_code)>>{
|
||||
detail::write_some_op<AsyncWriteStream, isRequest,
|
||||
Body, Fields, Decorator, Allocator, handler_type<
|
||||
WriteHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, sr}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write_header(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
error_code ec;
|
||||
write_header(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write_header(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
sr.split(true);
|
||||
detail::write_lambda<SyncWriteStream> f{stream};
|
||||
while(! sr.is_header_done())
|
||||
{
|
||||
sr.get(ec, f);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(f.invoked);
|
||||
sr.consume(f.bytes_transferred);
|
||||
}
|
||||
}
|
||||
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator, class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write_header(AsyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
sr.split(true);
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_op<AsyncWriteStream, isRequest, Body, Fields,
|
||||
Decorator, Allocator, detail::serializer_is_header_done,
|
||||
handler_type<WriteHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, sr}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
error_code ec;
|
||||
write(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
sr.split(false);
|
||||
detail::write_lambda<SyncWriteStream> f{stream};
|
||||
while(! sr.is_done())
|
||||
{
|
||||
sr.get(ec, f);
|
||||
if(ec)
|
||||
return;
|
||||
if(f.invoked)
|
||||
sr.consume(f.bytes_transferred);
|
||||
}
|
||||
if(sr.need_close())
|
||||
ec = error::end_of_stream;
|
||||
}
|
||||
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator, class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write(AsyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
sr.split(false);
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_op<AsyncWriteStream, isRequest, Body,
|
||||
Fields, Decorator, Allocator, detail::serializer_is_done,
|
||||
handler_type<WriteHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, sr}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
@@ -374,18 +750,10 @@ write(SyncWriteStream& stream,
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
auto sr = make_serializer(msg);
|
||||
for(;;)
|
||||
{
|
||||
#if 0
|
||||
sr.write_some(stream, ec);
|
||||
#else
|
||||
write_some(stream, sr, ec);
|
||||
#endif
|
||||
if(ec)
|
||||
return;
|
||||
if(sr.is_done())
|
||||
break;
|
||||
}
|
||||
write(stream, sr, ec);
|
||||
if(! ec)
|
||||
if(sr.need_close())
|
||||
ec = error::end_of_stream;
|
||||
}
|
||||
|
||||
template<class AsyncWriteStream,
|
||||
@@ -406,11 +774,10 @@ async_write(AsyncWriteStream& stream,
|
||||
"BodyReader requirements not met");
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_op<AsyncWriteStream, handler_type<
|
||||
detail::write_msg_op<AsyncWriteStream, handler_type<
|
||||
WriteHandler, void(error_code)>,
|
||||
isRequest, Body, Fields>{
|
||||
init.completion_handler, stream, msg}(
|
||||
error_code{});
|
||||
init.completion_handler, stream, msg}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
@@ -439,7 +806,7 @@ operator<<(std::ostream& os,
|
||||
beast::detail::sync_ostream oss{os};
|
||||
error_code ec;
|
||||
write(oss, msg, ec);
|
||||
if(ec && ec != boost::asio::error::eof)
|
||||
if(ec && ec != error::end_of_stream)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -34,13 +34,18 @@ namespace http {
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<bool isRequest, class Body, class Fields = fields>
|
||||
class message_parser
|
||||
: public basic_parser<isRequest,
|
||||
Body::writer::is_direct,
|
||||
message_parser<isRequest, Body, Fields>>
|
||||
message_parser<isRequest, Body, Fields>>
|
||||
{
|
||||
using base_type = basic_parser<isRequest, true,
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
|
||||
using base_type = basic_parser<isRequest,
|
||||
message_parser<isRequest, Body, Fields>>;
|
||||
|
||||
using writer_type = typename Body::writer;
|
||||
@@ -52,10 +57,6 @@ public:
|
||||
/// The type of message returned by the parser
|
||||
using value_type = message<isRequest, Body, Fields>;
|
||||
|
||||
/// The type of buffer sequence representing the body
|
||||
using mutable_buffers_type =
|
||||
typename writer_type::mutable_buffers_type;
|
||||
|
||||
/// Constructor (default)
|
||||
message_parser() = default;
|
||||
|
||||
@@ -99,9 +100,7 @@ public:
|
||||
#endif
|
||||
|
||||
/** Construct a message parser from a @ref header_parser.
|
||||
|
||||
@param parser The header parser to construct from.
|
||||
|
||||
@param args Optional arguments forwarded to the message
|
||||
constructor.
|
||||
*/
|
||||
@@ -152,8 +151,7 @@ public:
|
||||
|
||||
private:
|
||||
friend class basic_parser<
|
||||
isRequest, Body::writer::is_direct,
|
||||
message_parser>;
|
||||
isRequest, message_parser>;
|
||||
|
||||
void
|
||||
on_request(
|
||||
@@ -190,61 +188,25 @@ private:
|
||||
}
|
||||
|
||||
void
|
||||
on_body()
|
||||
{
|
||||
wr_.emplace(m_);
|
||||
wr_->init();
|
||||
}
|
||||
|
||||
void
|
||||
on_body(std::uint64_t content_length)
|
||||
{
|
||||
wr_.emplace(m_);
|
||||
wr_->init(content_length);
|
||||
}
|
||||
|
||||
void
|
||||
on_body(error_code& ec)
|
||||
{
|
||||
wr_.emplace(m_);
|
||||
wr_->init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
on_body(std::uint64_t content_length,
|
||||
error_code& ec)
|
||||
on_body(boost::optional<
|
||||
std::uint64_t> const& content_length,
|
||||
error_code& ec)
|
||||
{
|
||||
wr_.emplace(m_);
|
||||
wr_->init(content_length, ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
on_data(string_view const& s,
|
||||
error_code& ec)
|
||||
{
|
||||
BOOST_STATIC_ASSERT(! Body::writer::is_direct);
|
||||
wr_->write(s, ec);
|
||||
}
|
||||
|
||||
mutable_buffers_type
|
||||
on_prepare(std::size_t n)
|
||||
{
|
||||
return wr_->prepare(n);
|
||||
wr_->put(boost::asio::buffer(
|
||||
s.data(), s.size()), ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_commit(std::size_t n)
|
||||
{
|
||||
wr_->commit(n);
|
||||
}
|
||||
|
||||
void
|
||||
on_chunk(std::uint64_t,
|
||||
string_view const&,
|
||||
on_chunk(
|
||||
std::uint64_t, string_view const&,
|
||||
error_code&)
|
||||
{
|
||||
}
|
||||
@@ -253,28 +215,18 @@ private:
|
||||
on_complete(error_code& ec)
|
||||
{
|
||||
if(wr_)
|
||||
do_on_complete(ec,
|
||||
std::integral_constant<bool,
|
||||
Body::writer::is_direct>{});
|
||||
}
|
||||
|
||||
void
|
||||
do_on_complete(
|
||||
error_code& ec, std::true_type)
|
||||
{
|
||||
wr_->finish();
|
||||
}
|
||||
|
||||
void
|
||||
do_on_complete(
|
||||
error_code& ec, std::false_type)
|
||||
{
|
||||
wr_->finish(ec);
|
||||
if(ec)
|
||||
return;
|
||||
wr_->finish(ec);
|
||||
}
|
||||
};
|
||||
|
||||
/// A parser for producing HTTP/1 messages
|
||||
template<class Body, class Fields = fields>
|
||||
using request_parser = message_parser<true, Body, Fields>;
|
||||
|
||||
/// A parser for producing HTTP/1 messages
|
||||
template<class Body, class Fields = fields>
|
||||
using response_parser = message_parser<false, Body, Fields>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
|
||||
+186
-13
@@ -66,12 +66,12 @@ namespace http {
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser);
|
||||
basic_parser<isRequest, Derived>& parser);
|
||||
|
||||
/** Read some HTTP/1 message data from a stream.
|
||||
|
||||
@@ -122,12 +122,12 @@ read_some(
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Start an asynchronous operation to read some HTTP/1 message data from a stream.
|
||||
@@ -192,7 +192,7 @@ read_some(
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
@@ -203,7 +203,170 @@ async_return_type<
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read a header into an HTTP/1 parser from a stream.
|
||||
|
||||
This function synchronously reads from a stream and passes
|
||||
data to the specified parser. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li The parser indicates that the complete header is received
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function is implemented in terms of one or more calls
|
||||
to the stream's `read_some` function. The implementation may
|
||||
read additional octets that lie past the end of the object
|
||||
being parsed. This additional data is stored in the dynamic
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
If the end of the stream is reached during the read, the
|
||||
value @ref error::partial_message is indicated as the
|
||||
error if bytes have been processed, else the error
|
||||
@ref error::end_of_stream is indicated.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `false` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser);
|
||||
|
||||
/** Read a header into an HTTP/1 parser from a stream.
|
||||
|
||||
This function synchronously reads from a stream and passes
|
||||
data to the specified parser. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li The parser indicates that the complete header is received
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function is implemented in terms of one or more calls
|
||||
to the stream's `read_some` function. The implementation may
|
||||
read additional octets that lie past the end of the object
|
||||
being parsed. This additional data is stored in the dynamic
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
If the end of the stream is reached during the read, the
|
||||
value @ref error::partial_message is indicated as the
|
||||
error if bytes have been processed, else the error
|
||||
@ref error::end_of_stream is indicated.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `false` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Read a header into an HTTP/1 parser asynchronously from a stream.
|
||||
|
||||
This function is used to asynchronously read from a stream and
|
||||
pass the data to the specified parser. The function call always
|
||||
returns immediately. The asynchronous operation will continue
|
||||
until one of the following conditions is true:
|
||||
|
||||
@li The parser indicates that the complete header is received
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This operation is implemented in terms of one 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 operations until this operation completes.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the object being parsed. This additional data is stored
|
||||
in the stream buffer, which may be used in subsequent calls.
|
||||
|
||||
If the end of the stream is reached during the read, the
|
||||
value @ref error::partial_message is indicated as the
|
||||
error if bytes have been processed, else the error
|
||||
@ref error::end_of_stream is indicated.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b AsyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@param handler The handler to be called when the request
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `false` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code)>
|
||||
#endif
|
||||
async_read_header(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -241,16 +404,19 @@ async_read_some(
|
||||
@param parser The parser to use.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `true` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser);
|
||||
basic_parser<isRequest, Derived>& parser);
|
||||
|
||||
/** Read into an HTTP/1 parser from a stream.
|
||||
|
||||
@@ -285,16 +451,19 @@ read(
|
||||
@param parser The parser to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `true` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Read into an HTTP/1 parser asynchronously from a stream.
|
||||
@@ -342,11 +511,14 @@ read(
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `true` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
@@ -357,9 +529,11 @@ async_return_type<
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read an HTTP/1 message from a stream.
|
||||
|
||||
This function is used to synchronously read a message from
|
||||
@@ -522,7 +696,6 @@ async_read(
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/async_read.ipp>
|
||||
#include <beast/http/impl/read.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -65,7 +65,7 @@ struct empty_decorator
|
||||
if the contents of the message indicate that chunk encoding
|
||||
is required. If the semantics of the message indicate that
|
||||
the connection should be closed after the message is sent, the
|
||||
function @ref needs_close will return `true`.
|
||||
function @ref need_close will return `true`.
|
||||
|
||||
Upon construction, an optional chunk decorator may be
|
||||
specified. This decorator is a function object called with
|
||||
@@ -135,9 +135,11 @@ class serializer
|
||||
|
||||
enum
|
||||
{
|
||||
do_init = 0,
|
||||
do_header_only = 10,
|
||||
do_header = 20,
|
||||
do_construct = 0,
|
||||
|
||||
do_init = 10,
|
||||
do_header_only = 20,
|
||||
do_header = 30,
|
||||
do_body = 40,
|
||||
|
||||
do_init_c = 50,
|
||||
@@ -191,7 +193,7 @@ class serializer
|
||||
buffer_type b_;
|
||||
boost::variant<boost::blank,
|
||||
cb0_t, cb1_t, ch0_t, ch1_t, ch2_t> v_;
|
||||
int s_;
|
||||
int s_ = do_construct;
|
||||
bool split_ = is_deferred::value;
|
||||
bool header_done_ = false;
|
||||
bool chunked_;
|
||||
@@ -201,8 +203,13 @@ class serializer
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
@param msg The message to serialize, which must
|
||||
remain valid for the lifetime of the serializer.
|
||||
The implementation guarantees that the message passed on
|
||||
construction will not be accessed until the first call to
|
||||
@ref get. This allows the message to be lazily created.
|
||||
For example, if the header is filled in before serialization.
|
||||
|
||||
@param msg The message to serialize, which must remain valid
|
||||
for the lifetime of the serializer.
|
||||
|
||||
@param decorator An optional decorator to use.
|
||||
|
||||
@@ -268,7 +275,7 @@ public:
|
||||
function returns `true`.
|
||||
*/
|
||||
bool
|
||||
needs_close() const
|
||||
need_close() const
|
||||
{
|
||||
return close_;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#define BEAST_HTTP_STRING_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
@@ -30,68 +30,6 @@ struct string_body
|
||||
/// The type of the body member when used in a message.
|
||||
using value_type = std::string;
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm used store buffers in this body
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
std::size_t len_ = 0;
|
||||
|
||||
public:
|
||||
static bool constexpr is_direct = true;
|
||||
|
||||
using mutable_buffers_type =
|
||||
boost::asio::mutable_buffers_1;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest,
|
||||
string_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(std::uint64_t content_length)
|
||||
{
|
||||
if(content_length >
|
||||
(std::numeric_limits<std::size_t>::max)())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"Content-Length overflow"});
|
||||
body_.reserve(static_cast<
|
||||
std::size_t>(content_length));
|
||||
}
|
||||
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n)
|
||||
{
|
||||
body_.resize(len_ + n);
|
||||
return {&body_[len_], n};
|
||||
}
|
||||
|
||||
void
|
||||
commit(std::size_t n)
|
||||
{
|
||||
if(body_.size() > len_ + n)
|
||||
body_.resize(len_ + n);
|
||||
len_ = body_.size();
|
||||
}
|
||||
|
||||
void
|
||||
finish()
|
||||
{
|
||||
body_.resize(len_);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm to obtain buffers representing the body
|
||||
using reader = implementation_defined;
|
||||
@@ -134,6 +72,69 @@ struct string_body
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm used store buffers in this body
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, string_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> content_length, error_code& ec)
|
||||
{
|
||||
if(content_length)
|
||||
{
|
||||
if(*content_length > (std::numeric_limits<
|
||||
std::size_t>::max)())
|
||||
{
|
||||
ec = make_error_code(
|
||||
errc::not_enough_memory);
|
||||
return;
|
||||
}
|
||||
body_.reserve(static_cast<
|
||||
std::size_t>(*content_length));
|
||||
}
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::buffer_copy;
|
||||
auto const n = buffer_size(buffers);
|
||||
auto const len = body_.size();
|
||||
try
|
||||
{
|
||||
body_.resize(len + n);
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return;
|
||||
}
|
||||
buffer_copy(boost::asio::buffer(
|
||||
&body_[0] + len, n), buffers);
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // http
|
||||
|
||||
@@ -114,28 +114,24 @@ struct is_body_reader<T, beast::detail::void_t<
|
||||
template<class T>
|
||||
struct is_body_writer : std::integral_constant<bool, ...> {};
|
||||
#else
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct is_body_writer : std::true_type {};
|
||||
template<class T, class = void>
|
||||
struct is_body_writer : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_body_writer<T, beast::detail::void_t<decltype(
|
||||
std::declval<typename T::writer::mutable_buffers_type>(),
|
||||
std::declval<T::writer>().init(
|
||||
std::declval<boost::optional<std::uint64_t>>()),
|
||||
std::declval<T::writer>().prepare(
|
||||
std::declval<std::size_t>()),
|
||||
std::declval<T::writer>().commit(
|
||||
std::declval<std::size_t>()),
|
||||
std::declval<T::writer>().finish()
|
||||
)> > : std::integral_constant<bool,
|
||||
is_mutable_buffer_sequence<
|
||||
typename T::writer::mutable_buffers_type>::value &&
|
||||
std::is_convertible<decltype(
|
||||
std::declval<T::writer>().prepare(
|
||||
std::declval<std::size_t>())),
|
||||
typename T::writer::mutable_buffers_type
|
||||
>::value>
|
||||
|
||||
std::declval<typename T::writer&>().init(
|
||||
std::declval<boost::optional<std::uint64_t>>(),
|
||||
std::declval<error_code&>()),
|
||||
std::declval<typename T::writer&>().put(
|
||||
std::declval<boost::asio::const_buffers_1>(),
|
||||
std::declval<error_code&>()),
|
||||
std::declval<typename T::writer&>().finish(
|
||||
std::declval<error_code&>()),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
true
|
||||
&& std::is_constructible<typename T::writer,
|
||||
message<true, T, detail::fields_model>&>::value
|
||||
>
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
||||
+327
-82
@@ -28,27 +28,35 @@
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Write some serialized message data to a stream.
|
||||
/** Write part of a message to a stream using a serializer.
|
||||
|
||||
This function is used to write serialized message data to the
|
||||
stream. The function call will block until one of the following
|
||||
conditions is true:
|
||||
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.
|
||||
|
||||
In order to completely serialize a message, this function
|
||||
should be called until `sr.is_done()` returns `true`.
|
||||
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, performing bounded work for each call. This
|
||||
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 write to. This type must
|
||||
satisfy the requirements of @b SyncWriteStream.
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@see @ref async_write_some, @ref serializer
|
||||
@see serializer
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
@@ -57,22 +65,29 @@ void
|
||||
write_some(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr);
|
||||
|
||||
/** Write part of a message to a stream using a serializer.
|
||||
|
||||
/** Write some serialized message data to a stream.
|
||||
|
||||
This function is used to write serialized message data to the
|
||||
stream. The function call will block until one of the following
|
||||
conditions is true:
|
||||
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.
|
||||
|
||||
In order to completely serialize a message, this function
|
||||
should be called until `sr.is_done()` returns `true`.
|
||||
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, performing bounded work for each call. This
|
||||
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 write to. This type must
|
||||
satisfy the requirements of @b SyncWriteStream.
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@@ -88,37 +103,47 @@ write_some(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
error_code& ec);
|
||||
|
||||
/** Start an asynchronous write of some serialized message data to a stream.
|
||||
/** Write part of a message to a stream asynchronously using a serializer.
|
||||
|
||||
This function is used to asynchronously write serialized
|
||||
message data to the stream. The function call always returns
|
||||
immediately. The asynchronous operation will continue until
|
||||
one of the following conditions is true:
|
||||
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.
|
||||
|
||||
In order to completely serialize a message, this function
|
||||
should be called until `sr.is_done()` returns `true`.
|
||||
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 write operations
|
||||
until this operation completes.
|
||||
|
||||
@param stream The stream to write to. This type must
|
||||
satisfy the requirements of @b SyncWriteStream.
|
||||
The amount of data actually transferred is controlled by the behavior
|
||||
of the underlying stream, performing bounded work for each call. This
|
||||
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 @b AsyncWriteStream concept.
|
||||
|
||||
@param sr The serializer to use for writing.
|
||||
@param sr The serializer to use.
|
||||
|
||||
@param handler The handler to be called when the request
|
||||
completes. Copies will be made of the handler as required. The
|
||||
equivalent function signature of the handler must be:
|
||||
@param handler The handler to be called when the operation
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& ec // Result of operation
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
@see @ref write_some, @ref serializer
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
@@ -132,98 +157,93 @@ async_write_some(AsyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
WriteHandler&& handler);
|
||||
|
||||
/** Write an HTTP/1 message to a stream.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
This function is used to write a message to a stream. The call
|
||||
will block until one of the following conditions is true:
|
||||
/** Write a header to a stream using a serializer.
|
||||
|
||||
@li The entire message is written.
|
||||
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.
|
||||
|
||||
The implementation will automatically perform chunk encoding if
|
||||
the contents of the message indicate that chunk encoding is required.
|
||||
If the semantics of the message indicate that the connection should
|
||||
be closed after the message is sent, the error thrown from this
|
||||
function will be `boost::asio::error::eof`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param msg The message to write.
|
||||
@param sr The serializer to use.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@note The implementation will call @ref serializer::split with
|
||||
the value `true` on the serializer passed in.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg);
|
||||
write_header(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr);
|
||||
|
||||
/** Write an HTTP/1 message to a stream.
|
||||
/** Write a header to a stream using a serializer.
|
||||
|
||||
This function is used to write a message to a stream. The call
|
||||
will block until one of the following conditions is true:
|
||||
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 entire message is written.
|
||||
@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.
|
||||
|
||||
The implementation will automatically perform chunk encoding if
|
||||
the contents of the message indicate that chunk encoding is required.
|
||||
If the semantics of the message indicate that the connection should
|
||||
be closed after the message is sent, the error returned from this
|
||||
function will be `boost::asio::error::eof`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param msg The message to write.
|
||||
@param sr The serializer to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
@param ec Set to indicate what error occurred, if any.
|
||||
|
||||
@note The implementation will call @ref serializer::split with
|
||||
the value `true` on the serializer passed in.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
write_header(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
error_code& ec);
|
||||
|
||||
/** Write an HTTP/1 message asynchronously to a stream.
|
||||
/** Write a header to a stream asynchronously using a serializer.
|
||||
|
||||
This function is used to asynchronously write a message to
|
||||
a stream. The function call always returns immediately. The
|
||||
asynchronous operation will continue until one of the following
|
||||
conditions is true:
|
||||
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 entire message is written.
|
||||
@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 `async_write_some` functions, and is known as a
|
||||
<em>composed operation</em>. The program must ensure that the
|
||||
stream performs no other write operations until this operation
|
||||
completes.
|
||||
|
||||
The implementation will automatically perform chunk encoding if
|
||||
the contents of the message indicate that chunk encoding is required.
|
||||
If the semantics of the message indicate that the connection should
|
||||
be closed after the message is sent, the operation will complete with
|
||||
the error set to `boost::asio::error::eof`.
|
||||
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 write operations
|
||||
until this operation completes.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b AsyncWriteStream concept.
|
||||
|
||||
@param msg The message to write. The object must remain valid
|
||||
at least until the completion handler is called; ownership is
|
||||
not transferred.
|
||||
@param sr The serializer to use.
|
||||
|
||||
@param handler The handler to be called when the operation
|
||||
completes. Copies will be made of the handler as required.
|
||||
@@ -235,6 +255,231 @@ write(SyncWriteStream& stream,
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
@note The implementation will call @ref serializer::split with
|
||||
the value `true` on the serializer passed in.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator, class WriteHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
#endif
|
||||
async_write_header(AsyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
WriteHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** 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 @b SyncWriteStream concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& 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 @b SyncWriteStream concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& 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 write operations
|
||||
until this operation completes.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b AsyncWriteStream concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@param handler The handler to be called when the operation
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator, class WriteHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
#endif
|
||||
async_write(AsyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
WriteHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** 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. If the semantics of the
|
||||
message indicate that the connection should be closed after the message is
|
||||
sent, the error delivered by this function will be @ref error::end_of_stream
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param msg The message to write.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@see @ref message
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
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. If the semantics of the
|
||||
message indicate that the connection should be closed after the message is
|
||||
sent, the error delivered by this function will be @ref error::end_of_stream
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param msg The message to write.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@see @ref message
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
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 write operations
|
||||
until this operation completes. The algorithm will use a temporary
|
||||
@ref serializer with an empty chunk decorator to produce buffers. If
|
||||
the semantics of the message indicate that the connection should be
|
||||
closed after the message is sent, the error delivered by this function
|
||||
will be @ref error::end_of_stream
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b AsyncWriteStream concept.
|
||||
|
||||
@param msg The message to write. The object must remain valid at least
|
||||
until the completion handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The handler to be called when the operation
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
@see @ref message
|
||||
*/
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// A container that holds a suspended, asynchronous composed
|
||||
// operation. The contained object may be invoked later to
|
||||
// operation. The contained object may be invoked later to
|
||||
// resume the operation, or the container may be destroyed.
|
||||
//
|
||||
class pausation
|
||||
|
||||
@@ -281,7 +281,7 @@ operator()(error_code ec,
|
||||
|
||||
case 1:
|
||||
{
|
||||
BOOST_ASSERT(d.p.got_header());
|
||||
BOOST_ASSERT(d.p.is_header_done());
|
||||
d.ws.stream_.buffer().consume(bytes_used);
|
||||
// Arguments from our state must be
|
||||
// moved to the stack before releasing
|
||||
|
||||
@@ -96,7 +96,7 @@ do_accept(
|
||||
next_layer(), stream_.buffer(), p, ec);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(p.got_header());
|
||||
BOOST_ASSERT(p.is_header_done());
|
||||
stream_.buffer().consume(bytes_used);
|
||||
do_accept(p.get(), decorator, ec);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user