forked from boostorg/beast
New HTTP interfaces (API Change):
fix #123 fix #154 fix #265 This completely replaces the HTTP parser used to read and parse incoming messages. The new version is faster than the old one, and written in fewer lines. A summary of changes: * parse and async_parse renamed to read and async_read * basic_parser is optimized for linear buffers, use flat_streambuf for the best performance with these functions: - http::read - http::read_some - http::async_read - http::async_read_some * The overloads of read() and async_read() which take just a header have been removed, since they would throw away important parse metadata. * The derived class callbacks for basic_parser have been streamlined. All strings passed to callbacks are presented in their entirety, instead of being provided in pieces. These changes allow use-cases that were previously difficult or impossible, such as: - Efficient relaying - Late body type commitment - Expect: 100-continue handling
This commit is contained in:
@@ -10,17 +10,15 @@
|
||||
|
||||
#include <boost/range/algorithm/equal.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
inline
|
||||
char
|
||||
tolower(char c)
|
||||
tolower(signed char c)
|
||||
{
|
||||
static std::array<std::uint8_t, 256> constexpr tab = {{
|
||||
static unsigned char constexpr tab[256] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
@@ -37,8 +35,9 @@ tolower(char c)
|
||||
208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
|
||||
224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
|
||||
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
|
||||
}};
|
||||
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
|
||||
};
|
||||
return static_cast<char>(
|
||||
tab[static_cast<unsigned char>(c)]);
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
#include <beast/config.hpp>
|
||||
|
||||
#include <beast/http/basic_fields.hpp>
|
||||
#include <beast/http/basic_parser_v1.hpp>
|
||||
#include <beast/http/basic_parser.hpp>
|
||||
#include <beast/http/chunk_encode.hpp>
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/fields.hpp>
|
||||
#include <beast/http/header_parser.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/parse.hpp>
|
||||
#include <beast/http/parse_error.hpp>
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
#include <beast/http/message_parser.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/reason.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
|
||||
@@ -33,30 +33,47 @@ private:
|
||||
|
||||
class reader
|
||||
{
|
||||
value_type& sb_;
|
||||
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
|
||||
reader(message<isRequest,
|
||||
basic_dynabuf_body, Fields>& m) noexcept
|
||||
: sb_(m.body)
|
||||
basic_dynabuf_body, Fields>& msg)
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code&) noexcept
|
||||
init()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
write(void const* data,
|
||||
std::size_t size, error_code&) noexcept
|
||||
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()
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
sb_.commit(buffer_copy(
|
||||
sb_.prepare(size), buffer(data, size)));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,643 @@
|
||||
//
|
||||
// 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_BASIC_PARSER_HPP
|
||||
#define BEAST_HTTP_BASIC_PARSER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/detail/basic_parser.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
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
|
||||
HTTP/1 wire format. It allocates no memory when input is
|
||||
presented as a single contiguous buffer, and uses minimal
|
||||
state. It will handle chunked encoding and it understands
|
||||
the semantics of the Connection, Content-Length, and Upgrade
|
||||
fields.
|
||||
|
||||
The interface uses CRTP (Curiously Recurring Template Pattern).
|
||||
To use this class, derive from @ref basic_parser. When bytes
|
||||
are presented, the implementation will make a series of zero
|
||||
or more calls to derived class members functions (referred to
|
||||
as "callbacks" from here on) matching a specific signature.
|
||||
|
||||
Every callback must be provided by the derived class, or else
|
||||
a compilation error will be generated. This exemplar shows
|
||||
the signature and description of the callbacks required in
|
||||
the derived class.
|
||||
|
||||
@par Derived Example
|
||||
|
||||
@code
|
||||
template<bool isRequest>
|
||||
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(
|
||||
boost::string_ref const& method,
|
||||
boost::string_ref const& path,
|
||||
int version,
|
||||
error_code& ec);
|
||||
|
||||
// When isRequest == false, called
|
||||
// after the Status Line is received.
|
||||
//
|
||||
void
|
||||
on_response(
|
||||
int status,
|
||||
boost::string_ref const& reason,
|
||||
int version,
|
||||
error_code& ec);
|
||||
|
||||
// Called after receiving a field/value pair.
|
||||
//
|
||||
void
|
||||
on_field(
|
||||
boost::string_ref const& name,
|
||||
boost::string_ref const& value,
|
||||
error_code& ec);
|
||||
|
||||
// Called after the header is complete.
|
||||
//
|
||||
void
|
||||
on_header(
|
||||
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();
|
||||
|
||||
// Called zero or more times to provide body data.
|
||||
//
|
||||
// Only used if isDirect == false
|
||||
//
|
||||
void
|
||||
on_data(
|
||||
boost::string_ref const& 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
|
||||
// chunk.
|
||||
//
|
||||
void
|
||||
on_chunk(
|
||||
std::uint64_t length, // Length of this chunk
|
||||
boost::string_ref const& ext, // The chunk extensions, if any
|
||||
error_code& ec);
|
||||
|
||||
// Called once when the message is complete.
|
||||
// This will be called even if there is no body.
|
||||
//
|
||||
void
|
||||
on_complete(error_code& ec);
|
||||
};
|
||||
@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.
|
||||
|
||||
The parser is optimized for the case where the input buffer
|
||||
sequence consists of a single contiguous buffer. The
|
||||
@ref beast::flat_streambuf class is provided, which guarantees
|
||||
that the input sequence of the stream buffer will be represented
|
||||
by exactly one contiguous buffer. To ensure the optimum performance
|
||||
of the parser, use @ref beast::flat_streambuf with HTTP algorithms
|
||||
such as @ref beast::http::read, @ref beast::http::read_some,
|
||||
@ref beast::http::async_read, and @ref beast::http::async_read_some.
|
||||
Alternatively, the caller may use custom techniques to ensure that
|
||||
the structured portion of the HTTP message (header or chunk header)
|
||||
is contained in a linear buffer.
|
||||
|
||||
@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>
|
||||
class basic_parser
|
||||
: private detail::basic_parser_base
|
||||
{
|
||||
template<bool OtherIsRequest,
|
||||
bool OtherIsDirect, 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;
|
||||
|
||||
// The parser has read at least one byte
|
||||
static unsigned constexpr flagGotSome = 1<< 2;
|
||||
|
||||
// Message semantics indicate a body is expected.
|
||||
// cleared if flagSkipBody set
|
||||
//
|
||||
static unsigned constexpr flagHasBody = 1<< 3;
|
||||
|
||||
static unsigned constexpr flagHTTP11 = 1<< 4;
|
||||
static unsigned constexpr flagNeedEOF = 1<< 5;
|
||||
static unsigned constexpr flagExpectCRLF = 1<< 6;
|
||||
static unsigned constexpr flagFinalChunk = 1<< 7;
|
||||
static unsigned constexpr flagConnectionClose = 1<< 8;
|
||||
static unsigned constexpr flagConnectionUpgrade = 1<< 9;
|
||||
static unsigned constexpr flagConnectionKeepAlive = 1<< 10;
|
||||
static unsigned constexpr flagContentLength = 1<< 11;
|
||||
static unsigned constexpr flagChunked = 1<< 12;
|
||||
static unsigned constexpr flagUpgrade = 1<< 13;
|
||||
|
||||
std::uint64_t len_; // size of chunk or body
|
||||
std::unique_ptr<char[]> buf_;
|
||||
std::size_t buf_len_ = 0;
|
||||
std::size_t skip_ = 0; // search from here
|
||||
std::size_t x_; // scratch variable
|
||||
unsigned f_ = 0; // flags
|
||||
parse_state state_ = parse_state::header;
|
||||
boost::string_ref ext_;
|
||||
boost::string_ref body_;
|
||||
|
||||
public:
|
||||
/// Copy constructor (disallowed)
|
||||
basic_parser(basic_parser const&) = delete;
|
||||
|
||||
/// Copy assignment (disallowed)
|
||||
basic_parser& operator=(basic_parser const&) = delete;
|
||||
|
||||
/// Default constructor
|
||||
basic_parser() = default;
|
||||
|
||||
/// `true` if this parser parses requests, `false` for responses.
|
||||
static bool constexpr is_request = isRequest;
|
||||
|
||||
/// Destructor
|
||||
~basic_parser() = default;
|
||||
|
||||
/** Move constructor
|
||||
|
||||
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>&&);
|
||||
|
||||
/** 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_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
|
||||
{
|
||||
return state_;
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is complete.
|
||||
|
||||
The message is complete after a full header is
|
||||
parsed and one of the following is true:
|
||||
|
||||
@li @ref skip_body was called
|
||||
|
||||
@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.
|
||||
*/
|
||||
bool
|
||||
is_complete() const
|
||||
{
|
||||
return state_ == parse_state::complete;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is an upgrade message.
|
||||
|
||||
@note Only valid after parsing a complete header.
|
||||
*/
|
||||
bool
|
||||
is_upgrade() const
|
||||
{
|
||||
return (f_ & flagConnectionUpgrade) != 0;
|
||||
}
|
||||
|
||||
/** Returns `true` if keep-alive is specified
|
||||
|
||||
@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.
|
||||
*/
|
||||
bool
|
||||
is_chunked() const
|
||||
{
|
||||
return (f_ & flagChunked) != 0;
|
||||
}
|
||||
|
||||
/** Write part of a buffer sequence to the parser.
|
||||
|
||||
This function attempts to parse the HTTP message
|
||||
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.
|
||||
|
||||
@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.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write(ConstBufferSequence const& buffers, error_code& ec);
|
||||
|
||||
#if ! GENERATING_DOCS
|
||||
std::size_t
|
||||
write(boost::asio::const_buffers_1 const& buffer,
|
||||
error_code& ec);
|
||||
#endif
|
||||
|
||||
/** Inform the parser that the end of stream was reached.
|
||||
|
||||
In certain cases, HTTP needs to know where the end of
|
||||
the stream is. For example, sometimes servers send
|
||||
responses without Content-Length and expect the client
|
||||
to consume input (for the body) until EOF. Callbacks
|
||||
and errors will still be processed as usual.
|
||||
|
||||
This is typically called when a read from the
|
||||
underlying stream object sets the error code to
|
||||
`boost::asio::error::eof`.
|
||||
|
||||
@note Only valid after parsing a complete header.
|
||||
|
||||
@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.
|
||||
*/
|
||||
boost::string_ref const&
|
||||
body() const
|
||||
{
|
||||
// This function not available when isDirect==true
|
||||
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.
|
||||
*/
|
||||
boost::string_ref const&
|
||||
chunk_extension() const
|
||||
{
|
||||
// This function not available when isDirect==true
|
||||
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& dynabuf);
|
||||
|
||||
/** Returns a set of buffers for storing body data.
|
||||
|
||||
@note This member function is only available when
|
||||
`isDirect==true`.
|
||||
|
||||
@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.
|
||||
*/
|
||||
template<class MutableBufferSequence>
|
||||
void
|
||||
prepare_body(boost::optional<
|
||||
MutableBufferSequence>& buffers, std::size_t limit);
|
||||
|
||||
/** Commit body data.
|
||||
|
||||
@note This member function is only available when
|
||||
`isDirect==true`.
|
||||
*/
|
||||
void
|
||||
commit_body(std::size_t n);
|
||||
|
||||
/** Indicate that body octets have been consumed.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/** Consume all remaining body data.
|
||||
|
||||
This function instructs the parser to advance the
|
||||
state past any expected body octets. Callers who
|
||||
wish to read and process the body themselves will
|
||||
call this function.
|
||||
*/
|
||||
void
|
||||
consume_body(error_code& ec);
|
||||
|
||||
private:
|
||||
inline
|
||||
Derived&
|
||||
impl()
|
||||
{
|
||||
return *static_cast<Derived*>(this);
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
boost::string_ref
|
||||
maybe_flatten(
|
||||
ConstBufferSequence const& buffers);
|
||||
|
||||
std::size_t
|
||||
do_write(boost::asio::const_buffers_1 const& buffer,
|
||||
error_code& ec, std::true_type);
|
||||
|
||||
std::size_t
|
||||
do_write(boost::asio::const_buffers_1 const& buffer,
|
||||
error_code& ec, std::false_type);
|
||||
|
||||
void
|
||||
parse_startline(char const*& it,
|
||||
int& version, int& status,
|
||||
error_code& ec, std::true_type);
|
||||
|
||||
void
|
||||
parse_startline(char const*& it,
|
||||
int& version, int& status,
|
||||
error_code& ec, std::false_type);
|
||||
|
||||
void
|
||||
parse_fields(char const*& it,
|
||||
char const* last, error_code& ec);
|
||||
|
||||
void
|
||||
do_field(
|
||||
boost::string_ref const& name,
|
||||
boost::string_ref 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);
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/basic_parser.ipp>
|
||||
|
||||
#endif
|
||||
@@ -1,856 +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_BASIC_PARSER_v1_HPP
|
||||
#define BEAST_HTTP_BASIC_PARSER_v1_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/parse_error.hpp>
|
||||
#include <beast/http/rfc7230.hpp>
|
||||
#include <beast/http/detail/basic_parser_v1.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Parse flags
|
||||
|
||||
The set of parser bit flags are returned by @ref basic_parser_v1::flags.
|
||||
*/
|
||||
enum parse_flag
|
||||
{
|
||||
chunked = 1,
|
||||
connection_keep_alive = 2,
|
||||
connection_close = 4,
|
||||
connection_upgrade = 8,
|
||||
trailing = 16,
|
||||
upgrade = 32,
|
||||
skipbody = 64,
|
||||
contentlength = 128,
|
||||
paused = 256
|
||||
};
|
||||
|
||||
/** Body maximum size option.
|
||||
|
||||
Sets the maximum number of cumulative bytes allowed including
|
||||
all body octets. Octets in chunk-encoded bodies are counted
|
||||
after decoding. A value of zero indicates no limit on
|
||||
the number of body octets.
|
||||
|
||||
The default body maximum size for requests is 4MB (four
|
||||
megabytes or 4,194,304 bytes) and unlimited for responses.
|
||||
|
||||
@note Objects of this type are used with @ref basic_parser_v1::set_option.
|
||||
*/
|
||||
struct body_max_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
body_max_size(std::size_t v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** Header maximum size option.
|
||||
|
||||
Sets the maximum number of cumulative bytes allowed
|
||||
including all header octets. A value of zero indicates
|
||||
no limit on the number of header octets.
|
||||
|
||||
The default header maximum size is 16KB (16,384 bytes).
|
||||
|
||||
@note Objects of this type are used with @ref basic_parser_v1::set_option.
|
||||
*/
|
||||
struct header_max_size
|
||||
{
|
||||
std::size_t value;
|
||||
|
||||
explicit
|
||||
header_max_size(std::size_t v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** A value indicating how the parser should treat the body.
|
||||
|
||||
This value is returned from the `on_header` callback in
|
||||
the derived class. It controls what the parser does next
|
||||
in terms of the message body.
|
||||
*/
|
||||
enum class body_what
|
||||
{
|
||||
/** The parser should expect a body, keep reading.
|
||||
*/
|
||||
normal,
|
||||
|
||||
/** Skip parsing of the body.
|
||||
|
||||
When returned by `on_header` this causes parsing to
|
||||
complete and control to return to the caller. This
|
||||
could be used when sending a response to a HEAD
|
||||
request, for example.
|
||||
*/
|
||||
skip,
|
||||
|
||||
/** The message represents an UPGRADE request.
|
||||
|
||||
When returned by `on_body_prepare` this causes parsing
|
||||
to complete and control to return to the caller.
|
||||
*/
|
||||
upgrade,
|
||||
|
||||
/** Suspend parsing before reading the body.
|
||||
|
||||
When returned by `on_body_prepare` this causes parsing
|
||||
to pause. Control is returned to the caller, and the
|
||||
parser state is preserved such that a subsequent call
|
||||
to the parser will begin reading the message body.
|
||||
|
||||
This could be used by callers to inspect the HTTP
|
||||
header before committing to read the body. For example,
|
||||
to choose the body type based on the fields. Or to
|
||||
respond to an Expect: 100-continue request.
|
||||
*/
|
||||
pause
|
||||
};
|
||||
|
||||
/// The value returned when no content length is known or applicable.
|
||||
static std::uint64_t constexpr no_content_length =
|
||||
(std::numeric_limits<std::uint64_t>::max)();
|
||||
|
||||
/** A parser for decoding HTTP/1 wire format messages.
|
||||
|
||||
This parser is designed to efficiently parse messages in the
|
||||
HTTP/1 wire format. It allocates no memory and uses minimal
|
||||
state. It will handle chunked encoding and it understands the
|
||||
semantics of the Connection and Content-Length header fields.
|
||||
|
||||
The interface uses CRTP (Curiously Recurring Template Pattern).
|
||||
To use this class, derive from basic_parser. When bytes are
|
||||
presented, the implementation will make a series of zero or
|
||||
more calls to derived class members functions (referred to as
|
||||
"callbacks" from here on) matching a specific signature.
|
||||
|
||||
Every callback must be provided by the derived class, or else
|
||||
a compilation error will be generated. This exemplar shows
|
||||
the signature and description of the callbacks required in
|
||||
the derived class.
|
||||
|
||||
@code
|
||||
template<bool isRequest>
|
||||
struct exemplar : basic_parser_v1<isRequest, exemplar>
|
||||
{
|
||||
// Called when the first valid octet of a new message is received
|
||||
//
|
||||
void on_start(error_code&);
|
||||
|
||||
// Called for each piece of the Request-Method
|
||||
//
|
||||
void on_method(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called for each piece of the Request-URI
|
||||
//
|
||||
void on_uri(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called for each piece of the reason-phrase
|
||||
//
|
||||
void on_reason(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called after the entire Request-Line has been parsed successfully.
|
||||
//
|
||||
void on_request(error_code&);
|
||||
|
||||
// Called after the entire Response-Line has been parsed successfully.
|
||||
//
|
||||
void on_response(error_code&);
|
||||
|
||||
// Called for each piece of the current header field.
|
||||
//
|
||||
void on_field(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called for each piece of the current header value.
|
||||
//
|
||||
void on_value(boost::string_ref const&, error_code&)
|
||||
|
||||
// Called when the entire header has been parsed successfully.
|
||||
//
|
||||
void
|
||||
on_header(std::uint64_t content_length, error_code&);
|
||||
|
||||
// Called after on_header, before the body is parsed
|
||||
//
|
||||
body_what
|
||||
on_body_what(std::uint64_t content_length, error_code&);
|
||||
|
||||
// Called for each piece of the body.
|
||||
//
|
||||
// If the header indicates chunk encoding, the chunk
|
||||
// encoding is removed from the buffer before being
|
||||
// passed to the callback.
|
||||
//
|
||||
void on_body(boost::string_ref const&, error_code&);
|
||||
|
||||
// Called when the entire message has been parsed successfully.
|
||||
// At this point, @ref complete returns `true`, and the parser
|
||||
// is ready to parse another message if `keep_alive` would
|
||||
// return `true`.
|
||||
//
|
||||
void on_complete(error_code&) {}
|
||||
};
|
||||
@endcode
|
||||
|
||||
The return value of `on_body_what` is special, it controls
|
||||
whether or not the parser should expect a body. See @ref body_what
|
||||
for choices of the return value.
|
||||
|
||||
If a callback sets an error, parsing stops at the current octet
|
||||
and the error is returned to the caller. Callbacks must not throw
|
||||
exceptions.
|
||||
|
||||
@tparam isRequest A `bool` indicating whether the parser will be
|
||||
presented with request or response message.
|
||||
|
||||
@tparam Derived The derived class type. This is part of the
|
||||
Curiously Recurring Template Pattern interface.
|
||||
*/
|
||||
template<bool isRequest, class Derived>
|
||||
class basic_parser_v1 : public detail::parser_base
|
||||
{
|
||||
private:
|
||||
template<bool, class>
|
||||
friend class basic_parser_v1;
|
||||
|
||||
using self = basic_parser_v1;
|
||||
typedef void(self::*pmf_t)(error_code&, boost::string_ref const&);
|
||||
|
||||
enum field_state : std::uint8_t
|
||||
{
|
||||
h_general = 0,
|
||||
h_C,
|
||||
h_CO,
|
||||
h_CON,
|
||||
|
||||
h_matching_connection,
|
||||
h_matching_proxy_connection,
|
||||
h_matching_content_length,
|
||||
h_matching_transfer_encoding,
|
||||
h_matching_upgrade,
|
||||
|
||||
h_connection,
|
||||
h_content_length0,
|
||||
h_content_length,
|
||||
h_content_length_ows,
|
||||
h_transfer_encoding,
|
||||
h_upgrade,
|
||||
|
||||
h_matching_transfer_encoding_chunked,
|
||||
h_matching_transfer_encoding_general,
|
||||
h_matching_connection_keep_alive,
|
||||
h_matching_connection_close,
|
||||
h_matching_connection_upgrade,
|
||||
|
||||
h_transfer_encoding_chunked,
|
||||
h_transfer_encoding_chunked_ows,
|
||||
|
||||
h_connection_keep_alive,
|
||||
h_connection_keep_alive_ows,
|
||||
h_connection_close,
|
||||
h_connection_close_ows,
|
||||
h_connection_upgrade,
|
||||
h_connection_upgrade_ows,
|
||||
h_connection_token,
|
||||
h_connection_token_ows
|
||||
};
|
||||
|
||||
std::size_t h_max_;
|
||||
std::size_t h_left_;
|
||||
std::size_t b_max_;
|
||||
std::size_t b_left_;
|
||||
std::uint64_t content_length_;
|
||||
pmf_t cb_;
|
||||
state s_ : 8;
|
||||
unsigned fs_ : 8;
|
||||
unsigned pos_ : 8; // position in field state
|
||||
unsigned http_major_ : 16;
|
||||
unsigned http_minor_ : 16;
|
||||
unsigned status_code_ : 16;
|
||||
unsigned flags_ : 9;
|
||||
bool upgrade_ : 1; // true if parser exited for upgrade
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
basic_parser_v1();
|
||||
|
||||
/// Copy constructor.
|
||||
template<class OtherDerived>
|
||||
basic_parser_v1(basic_parser_v1<
|
||||
isRequest, OtherDerived> const& other);
|
||||
|
||||
/// Copy assignment.
|
||||
template<class OtherDerived>
|
||||
basic_parser_v1& operator=(basic_parser_v1<
|
||||
isRequest, OtherDerived> const& other);
|
||||
|
||||
/** Set options on the parser.
|
||||
|
||||
@param args One or more parser options to set.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class... Args>
|
||||
void
|
||||
set_option(Args&&... args)
|
||||
#else
|
||||
template<class A1, class A2, class... An>
|
||||
void
|
||||
set_option(A1&& a1, A2&& a2, An&&... an)
|
||||
#endif
|
||||
{
|
||||
set_option(std::forward<A1>(a1));
|
||||
set_option(std::forward<A2>(a2),
|
||||
std::forward<An>(an)...);
|
||||
}
|
||||
|
||||
/// Set the header maximum size option
|
||||
void
|
||||
set_option(header_max_size const& o)
|
||||
{
|
||||
h_max_ = o.value;
|
||||
h_left_ = h_max_;
|
||||
}
|
||||
|
||||
/// Set the body maximum size option
|
||||
void
|
||||
set_option(body_max_size const& o)
|
||||
{
|
||||
b_max_ = o.value;
|
||||
b_left_ = b_max_;
|
||||
}
|
||||
|
||||
/// Returns internal flags associated with the parser.
|
||||
unsigned
|
||||
flags() const
|
||||
{
|
||||
return flags_;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message end is indicated by eof.
|
||||
|
||||
This function returns true if the semantics of the message require
|
||||
that the end of the message is signaled by an end of file. For
|
||||
example, if the message is a HTTP/1.0 message and the Content-Length
|
||||
is unspecified, the end of the message is indicated by an end of file.
|
||||
|
||||
@return `true` if write_eof must be used to indicate the message end.
|
||||
*/
|
||||
bool
|
||||
needs_eof() const
|
||||
{
|
||||
return needs_eof(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
/** Returns the major HTTP version number.
|
||||
|
||||
Examples:
|
||||
* Returns 1 for HTTP/1.1
|
||||
* Returns 1 for HTTP/1.0
|
||||
|
||||
@return The HTTP major version number.
|
||||
*/
|
||||
unsigned
|
||||
http_major() const
|
||||
{
|
||||
return http_major_;
|
||||
}
|
||||
|
||||
/** Returns the minor HTTP version number.
|
||||
|
||||
Examples:
|
||||
* Returns 1 for HTTP/1.1
|
||||
* Returns 0 for HTTP/1.0
|
||||
|
||||
@return The HTTP minor version number.
|
||||
*/
|
||||
unsigned
|
||||
http_minor() const
|
||||
{
|
||||
return http_minor_;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is an upgrade message.
|
||||
|
||||
A value of `true` indicates that the parser has successfully
|
||||
completed parsing a HTTP upgrade message.
|
||||
|
||||
@return `true` if the message is an upgrade message.
|
||||
*/
|
||||
bool
|
||||
upgrade() const
|
||||
{
|
||||
return upgrade_;
|
||||
}
|
||||
|
||||
/** Returns the numeric HTTP Status-Code of a response.
|
||||
|
||||
@return The Status-Code.
|
||||
*/
|
||||
unsigned
|
||||
status_code() const
|
||||
{
|
||||
return status_code_;
|
||||
}
|
||||
|
||||
/** Returns `true` if the connection should be kept open.
|
||||
|
||||
@note This function is only valid to call when the parser
|
||||
is complete.
|
||||
*/
|
||||
bool
|
||||
keep_alive() const;
|
||||
|
||||
/** Returns `true` if the parse has completed succesfully.
|
||||
|
||||
When the parse has completed successfully, and the semantics
|
||||
of the parsed message indicate that the connection is still
|
||||
active, a subsequent call to `write` will begin parsing a
|
||||
new message.
|
||||
|
||||
@return `true` If the parsing has completed successfully.
|
||||
*/
|
||||
bool
|
||||
complete() const
|
||||
{
|
||||
return
|
||||
s_ == s_restart ||
|
||||
s_ == s_closed_complete ||
|
||||
(flags_ & parse_flag::paused);
|
||||
}
|
||||
|
||||
/** Write a sequence of buffers to the parser.
|
||||
|
||||
@param buffers An object meeting the requirements of
|
||||
ConstBufferSequence that represents the input sequence.
|
||||
|
||||
@param ec Set to the error, if any error occurred.
|
||||
|
||||
@return The number of bytes consumed in the input sequence.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
#if BEAST_DOXYGEN
|
||||
std::size_t
|
||||
#else
|
||||
typename std::enable_if<
|
||||
! std::is_convertible<ConstBufferSequence,
|
||||
boost::asio::const_buffer>::value,
|
||||
std::size_t>::type
|
||||
#endif
|
||||
write(ConstBufferSequence const& buffers, error_code& ec);
|
||||
|
||||
/** Write a single buffer of data to the parser.
|
||||
|
||||
@param buffer The buffer to write.
|
||||
@param ec Set to the error, if any error occurred.
|
||||
|
||||
@return The number of bytes consumed in the buffer.
|
||||
*/
|
||||
std::size_t
|
||||
write(boost::asio::const_buffer const& buffer, error_code& ec);
|
||||
|
||||
/** Called to indicate the end of file.
|
||||
|
||||
HTTP needs to know where the end of the stream is. For example,
|
||||
sometimes servers send responses without Content-Length and
|
||||
expect the client to consume input (for the body) until EOF.
|
||||
Callbacks and errors will still be processed as usual.
|
||||
|
||||
@note This is typically called when a socket read returns eof.
|
||||
*/
|
||||
void
|
||||
write_eof(error_code& ec);
|
||||
|
||||
protected:
|
||||
/** Reset the parsing state.
|
||||
|
||||
The state of the parser is reset to expect the beginning of
|
||||
a new request or response. The old state is discarded.
|
||||
*/
|
||||
void
|
||||
reset();
|
||||
|
||||
private:
|
||||
Derived&
|
||||
impl()
|
||||
{
|
||||
return *static_cast<Derived*>(this);
|
||||
}
|
||||
|
||||
void
|
||||
reset(std::true_type)
|
||||
{
|
||||
s_ = s_req_start;
|
||||
}
|
||||
|
||||
void
|
||||
reset(std::false_type)
|
||||
{
|
||||
s_ = s_res_start;
|
||||
}
|
||||
|
||||
void
|
||||
init(std::true_type)
|
||||
{
|
||||
// Request: 16KB max header, 4MB max body
|
||||
h_max_ = 16 * 1024;
|
||||
b_max_ = 4 * 1024 * 1024;
|
||||
}
|
||||
|
||||
void
|
||||
init(std::false_type)
|
||||
{
|
||||
// Response: 16KB max header, unlimited body
|
||||
h_max_ = 16 * 1024;
|
||||
b_max_ = 0;
|
||||
}
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
init(std::integral_constant<bool, isRequest>{});
|
||||
reset();
|
||||
}
|
||||
|
||||
bool
|
||||
needs_eof(std::true_type) const;
|
||||
|
||||
bool
|
||||
needs_eof(std::false_type) const;
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_start : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_start<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_start(
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type { };
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_method : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_method<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_method(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_uri : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_uri<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_uri(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_reason : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_reason<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_reason(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_request : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_request<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_request(
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_response : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_response<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_response(
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_field : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_field<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_field(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_value : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_value<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_value(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_headers : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_headers<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_header(
|
||||
std::declval<std::uint64_t>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
// VFALCO Can we use std::is_detected? Is C++11 capable?
|
||||
template<class C>
|
||||
class check_on_body_what_t
|
||||
{
|
||||
template<class T, class R = std::is_convertible<decltype(
|
||||
std::declval<T>().on_body_what(
|
||||
std::declval<std::uint64_t>(),
|
||||
std::declval<error_code&>())),
|
||||
body_what>>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<C>(0));
|
||||
public:
|
||||
static bool const value = type::value;
|
||||
};
|
||||
template<class C>
|
||||
using check_on_body_what =
|
||||
std::integral_constant<bool, check_on_body_what_t<C>::value>;
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_body : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_body<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_body(
|
||||
std::declval<boost::string_ref>(),
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct check_on_complete : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct check_on_complete<T, beast::detail::void_t<decltype(
|
||||
std::declval<T>().on_complete(
|
||||
std::declval<error_code&>())
|
||||
)>> : std::true_type {};
|
||||
|
||||
void call_on_start(error_code& ec)
|
||||
{
|
||||
static_assert(check_on_start<Derived>::value,
|
||||
"on_start requirements not met");
|
||||
impl().on_start(ec);
|
||||
}
|
||||
|
||||
void call_on_method(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
static_assert(check_on_method<Derived>::value,
|
||||
"on_method requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_method(s, ec);
|
||||
}
|
||||
|
||||
void call_on_method(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_method(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
call_on_method(ec, s,
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_uri(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
static_assert(check_on_uri<Derived>::value,
|
||||
"on_uri requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_uri(s, ec);
|
||||
}
|
||||
|
||||
void call_on_uri(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_uri(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
call_on_uri(ec, s,
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_reason(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
static_assert(check_on_reason<Derived>::value,
|
||||
"on_reason requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_reason(s, ec);
|
||||
}
|
||||
|
||||
void call_on_reason(error_code&,
|
||||
boost::string_ref const&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_reason(error_code& ec, boost::string_ref const& s)
|
||||
{
|
||||
call_on_reason(ec, s,
|
||||
std::integral_constant<bool, ! isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_request(error_code& ec, std::true_type)
|
||||
{
|
||||
static_assert(check_on_request<Derived>::value,
|
||||
"on_request requirements not met");
|
||||
impl().on_request(ec);
|
||||
}
|
||||
|
||||
void call_on_request(error_code&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_request(error_code& ec)
|
||||
{
|
||||
call_on_request(ec,
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_response(error_code& ec, std::true_type)
|
||||
{
|
||||
static_assert(check_on_response<Derived>::value,
|
||||
"on_response requirements not met");
|
||||
impl().on_response(ec);
|
||||
}
|
||||
|
||||
void call_on_response(error_code&, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_response(error_code& ec)
|
||||
{
|
||||
call_on_response(ec,
|
||||
std::integral_constant<bool, ! isRequest>{});
|
||||
}
|
||||
|
||||
void call_on_field(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
static_assert(check_on_field<Derived>::value,
|
||||
"on_field requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_field(s, ec);
|
||||
}
|
||||
|
||||
void call_on_value(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
static_assert(check_on_value<Derived>::value,
|
||||
"on_value requirements not met");
|
||||
if(h_max_ && s.size() > h_left_)
|
||||
{
|
||||
ec = parse_error::header_too_big;
|
||||
return;
|
||||
}
|
||||
h_left_ -= s.size();
|
||||
impl().on_value(s, ec);
|
||||
}
|
||||
|
||||
void
|
||||
call_on_headers(error_code& ec)
|
||||
{
|
||||
static_assert(check_on_headers<Derived>::value,
|
||||
"on_header requirements not met");
|
||||
impl().on_header(content_length_, ec);
|
||||
}
|
||||
|
||||
body_what
|
||||
call_on_body_what(error_code& ec)
|
||||
{
|
||||
static_assert(check_on_body_what<Derived>::value,
|
||||
"on_body_what requirements not met");
|
||||
return impl().on_body_what(content_length_, ec);
|
||||
}
|
||||
|
||||
void call_on_body(error_code& ec,
|
||||
boost::string_ref const& s)
|
||||
{
|
||||
static_assert(check_on_body<Derived>::value,
|
||||
"on_body requirements not met");
|
||||
if(b_max_ && s.size() > b_left_)
|
||||
{
|
||||
ec = parse_error::body_too_big;
|
||||
return;
|
||||
}
|
||||
b_left_ -= s.size();
|
||||
impl().on_body(s, ec);
|
||||
}
|
||||
|
||||
void call_on_complete(error_code& ec)
|
||||
{
|
||||
static_assert(check_on_complete<Derived>::value,
|
||||
"on_complete requirements not met");
|
||||
impl().on_complete(ec);
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/basic_parser_v1.ipp>
|
||||
|
||||
#endif
|
||||
@@ -5,13 +5,15 @@
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_TYPE_CHECK_HPP
|
||||
#define BEAST_HTTP_TYPE_CHECK_HPP
|
||||
#ifndef BEAST_HTTP_CONCEPTS_HPP
|
||||
#define BEAST_HTTP_CONCEPTS_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -86,45 +88,6 @@ public:
|
||||
>;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class is_Parser
|
||||
{
|
||||
template<class U, class R =
|
||||
std::is_convertible<decltype(
|
||||
std::declval<U>().complete()),
|
||||
bool>>
|
||||
static R check1(int);
|
||||
template<class>
|
||||
static std::false_type check1(...);
|
||||
using type1 = decltype(check1<T>(0));
|
||||
|
||||
template<class U, class R =
|
||||
std::is_convertible<decltype(
|
||||
std::declval<U>().write(
|
||||
std::declval<boost::asio::const_buffers_1 const&>(),
|
||||
std::declval<error_code&>())),
|
||||
std::size_t>>
|
||||
static R check2(int);
|
||||
template<class>
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
template<class U, class R = decltype(
|
||||
std::declval<U>().write_eof(std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
static R check3(int);
|
||||
template<class>
|
||||
static std::false_type check3(...);
|
||||
using type3 = decltype(check3<T>(0));
|
||||
|
||||
public:
|
||||
using type = std::integral_constant<bool,
|
||||
type1::value &&
|
||||
type2::value &&
|
||||
type3::value
|
||||
>;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/// Determine if `T` meets the requirements of @b Body.
|
||||
@@ -183,19 +146,27 @@ template<class T, class M>
|
||||
struct is_Reader : std::integral_constant<bool, ...> {};
|
||||
#else
|
||||
template<class T, class M, class = beast::detail::void_t<>>
|
||||
struct is_Reader : std::false_type {};
|
||||
struct is_Reader : std::true_type {};
|
||||
|
||||
template<class T, class M>
|
||||
struct is_Reader<T, M, beast::detail::void_t<decltype(
|
||||
std::declval<typename T::mutable_buffers_type>(),
|
||||
std::declval<T>().init(
|
||||
std::declval<error_code&>()),
|
||||
std::declval<T>().write(
|
||||
std::declval<void const*>(),
|
||||
std::declval<std::size_t>(),
|
||||
std::declval<error_code&>())
|
||||
std::declval<boost::optional<std::uint64_t>>()),
|
||||
std::declval<T>().prepare(
|
||||
std::declval<std::size_t>()),
|
||||
std::declval<T>().commit(
|
||||
std::declval<std::size_t>()),
|
||||
std::declval<T>().finish()
|
||||
)> > : std::integral_constant<bool,
|
||||
std::is_nothrow_constructible<T, M&>::value
|
||||
>
|
||||
is_MutableBufferSequence<
|
||||
typename T::mutable_buffers_type>::value &&
|
||||
std::is_convertible<decltype(
|
||||
std::declval<T>().prepare(
|
||||
std::declval<std::size_t>())),
|
||||
typename T::mutable_buffers_type
|
||||
>::value>
|
||||
|
||||
{
|
||||
static_assert(std::is_same<
|
||||
typename M::body_type::reader, T>::value,
|
||||
@@ -217,14 +188,6 @@ struct is_Writer : std::integral_constant<bool, ...> {};
|
||||
using is_Writer = typename detail::is_Writer<T, M>::type;
|
||||
#endif
|
||||
|
||||
/// Determine if `T` meets the requirements of @b Parser.
|
||||
template<class T>
|
||||
#if BEAST_DOXYGEN
|
||||
struct is_Parser : std::integral_constant<bool, ...>{};
|
||||
#else
|
||||
using is_Parser = typename detail::is_Parser<T>::type;
|
||||
#endif
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
//
|
||||
// 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_DETAIL_BASIC_PARSED_LIST_HPP
|
||||
#define BEAST_HTTP_DETAIL_BASIC_PARSED_LIST_HPP
|
||||
|
||||
#include <beast/core/detail/empty_base_optimization.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
/** A list parser which presents the sequence as a container.
|
||||
*/
|
||||
template<class Policy>
|
||||
class basic_parsed_list
|
||||
{
|
||||
boost::string_ref s_;
|
||||
|
||||
public:
|
||||
/// The type of policy this list uses for parsing.
|
||||
using policy_type = Policy;
|
||||
|
||||
/// The type of each element in the list.
|
||||
using value_type = typename Policy::value_type;
|
||||
|
||||
/// A constant iterator to a list element.
|
||||
#if GENERATING_DOCS
|
||||
using const_iterator = implementation_defined;
|
||||
#else
|
||||
class const_iterator;
|
||||
#endif
|
||||
|
||||
class const_iterator
|
||||
: private beast::detail::
|
||||
empty_base_optimization<Policy>
|
||||
{
|
||||
basic_parsed_list const* list_ = nullptr;
|
||||
char const* it_ = nullptr;
|
||||
typename Policy::value_type v_;
|
||||
bool error_ = false;
|
||||
|
||||
public:
|
||||
using value_type =
|
||||
typename Policy::value_type;
|
||||
using reference = value_type const&;
|
||||
using pointer = value_type const*;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category =
|
||||
std::forward_iterator_tag;
|
||||
|
||||
const_iterator() = default;
|
||||
|
||||
bool
|
||||
operator==(
|
||||
const_iterator const& other) const
|
||||
{
|
||||
return
|
||||
other.list_ == list_ &&
|
||||
other.it_ == it_;
|
||||
}
|
||||
|
||||
bool
|
||||
operator!=(
|
||||
const_iterator const& other) const
|
||||
{
|
||||
return ! (*this == other);
|
||||
}
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return v_;
|
||||
}
|
||||
|
||||
const_iterator&
|
||||
operator++()
|
||||
{
|
||||
increment();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator
|
||||
operator++(int)
|
||||
{
|
||||
auto temp = *this;
|
||||
++(*this);
|
||||
return temp;
|
||||
}
|
||||
|
||||
bool
|
||||
error() const
|
||||
{
|
||||
return error_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parsed_list;
|
||||
|
||||
const_iterator(
|
||||
basic_parsed_list const& list, bool at_end)
|
||||
: list_(&list)
|
||||
, it_(at_end ? nullptr :
|
||||
list.s_.begin())
|
||||
{
|
||||
if(! at_end)
|
||||
increment();
|
||||
}
|
||||
|
||||
void
|
||||
increment()
|
||||
{
|
||||
if(! this->member()(
|
||||
v_, it_, list_->s_))
|
||||
{
|
||||
it_ = nullptr;
|
||||
error_ = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Construct a list from a string
|
||||
explicit
|
||||
basic_parsed_list(boost::string_ref const& s)
|
||||
: s_(s)
|
||||
{
|
||||
}
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator begin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator end() const;
|
||||
|
||||
/// Return a const iterator to the beginning of the list
|
||||
const_iterator cbegin() const;
|
||||
|
||||
/// Return a const iterator to the end of the list
|
||||
const_iterator cend() const;
|
||||
};
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
begin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, false};
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
end() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, true};
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
cbegin() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, false};
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
inline
|
||||
auto
|
||||
basic_parsed_list<Policy>::
|
||||
cend() const ->
|
||||
const_iterator
|
||||
{
|
||||
return const_iterator{*this, true};
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,446 @@
|
||||
//
|
||||
// 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_DETAIL_BASIC_PARSER_HPP
|
||||
#define BEAST_HTTP_DETAIL_BASIC_PARSER_HPP
|
||||
|
||||
#include <beast/core/detail/ci_char_traits.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/detail/rfc7230.hpp>
|
||||
#include <boost/utility/string_ref.hpp>
|
||||
#include <boost/version.hpp>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
/*
|
||||
Portions of this file based on code from picophttpparser,
|
||||
copyright notice below.
|
||||
https://github.com/h2o/picohttpparser
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
|
||||
* Shigeo Mitsunari
|
||||
*
|
||||
* The software is licensed under either the MIT License (below) or the Perl
|
||||
* license.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
#if __GNUC__ >= 3
|
||||
# define BEAST_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
# define BEAST_UNLIKELY(x) __builtin_expect(!!(x), 0)
|
||||
#else
|
||||
#define BEAST_LIKELY(x) (x)
|
||||
#define BEAST_UNLIKELY(x) (x)
|
||||
#endif
|
||||
|
||||
class basic_parser_base
|
||||
{
|
||||
protected:
|
||||
static
|
||||
bool
|
||||
is_pathchar(char c)
|
||||
{
|
||||
// VFALCO This looks the same as the one below...
|
||||
|
||||
// TEXT = <any OCTET except CTLs, and excluding LWS>
|
||||
static bool constexpr tab[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
is_value_char(char c)
|
||||
{
|
||||
// any OCTET except CTLs and LWS
|
||||
static bool constexpr tab[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
bool
|
||||
is_text(char c)
|
||||
{
|
||||
// VCHAR / SP / HT / obs-text
|
||||
static bool constexpr tab[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, // 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 112
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 128
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 144
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 160
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 176
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 192
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 208
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 224
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
static
|
||||
inline
|
||||
bool
|
||||
unhex(unsigned char& d, char c)
|
||||
{
|
||||
static signed char constexpr tab[256] = {
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 16
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 32
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, // 48
|
||||
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 64
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 80
|
||||
-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 96
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 112
|
||||
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 128
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 144
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 160
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 176
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 192
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 208
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 224
|
||||
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 // 240
|
||||
};
|
||||
d = static_cast<unsigned char>(
|
||||
tab[static_cast<unsigned char>(c)]);
|
||||
return d != static_cast<unsigned char>(-1);
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
is_digit(char c)
|
||||
{
|
||||
return static_cast<unsigned char>(c-'0') < 10;
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
is_print(char c)
|
||||
{
|
||||
return static_cast<unsigned char>(c-33) < 94;
|
||||
}
|
||||
|
||||
static
|
||||
boost::string_ref
|
||||
make_string(char const* first, char const* last)
|
||||
{
|
||||
return {first, static_cast<
|
||||
std::size_t>(last - first)};
|
||||
}
|
||||
|
||||
template<class = void>
|
||||
static
|
||||
bool
|
||||
strieq(boost::string_ref const& s1,
|
||||
boost::string_ref const& s2)
|
||||
{
|
||||
if(s1.size() != s2.size())
|
||||
return false;
|
||||
auto p1 = s1.data();
|
||||
auto p2 = s2.data();
|
||||
for(auto n = s1.size(); n--; ++p1, ++p2)
|
||||
if(*p1 != tolower(*p2))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
bool
|
||||
strieq(const char (&s1)[N],
|
||||
boost::string_ref const& s2)
|
||||
{
|
||||
return strieq({s1, N-1}, s2);
|
||||
}
|
||||
|
||||
template<class Iter, class Unsigned>
|
||||
static
|
||||
bool
|
||||
parse_dec(Iter it, Iter last, Unsigned& v)
|
||||
{
|
||||
if(! is_digit(*it))
|
||||
return false;
|
||||
v = *it - '0';
|
||||
for(;;)
|
||||
{
|
||||
if(! is_digit(*++it))
|
||||
break;
|
||||
auto const d = *it - '0';
|
||||
if(v > ((std::numeric_limits<
|
||||
Unsigned>::max)() - 10) / 10)
|
||||
return false;
|
||||
v = 10 * v + d;
|
||||
}
|
||||
return it == last;
|
||||
}
|
||||
|
||||
template<class Iter, class Unsigned>
|
||||
bool
|
||||
parse_hex(Iter& it, Unsigned& v)
|
||||
{
|
||||
unsigned char d;
|
||||
if(! unhex(d, *it))
|
||||
return false;
|
||||
v = d;
|
||||
for(;;)
|
||||
{
|
||||
if(! unhex(d, *++it))
|
||||
break;
|
||||
auto const v0 = v;
|
||||
v = 16 * v + d;
|
||||
if(v <= v0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
bool
|
||||
parse_crlf(char const*& it)
|
||||
{
|
||||
if(*it != '\r')
|
||||
return false;
|
||||
if(*++it != '\n')
|
||||
return false;
|
||||
++it;
|
||||
return true;
|
||||
}
|
||||
|
||||
static
|
||||
boost::string_ref
|
||||
parse_method(char const*& it)
|
||||
{
|
||||
auto const first = it;
|
||||
while(detail::is_tchar(*it))
|
||||
++it;
|
||||
return {first, static_cast<
|
||||
boost::string_ref::size_type>(
|
||||
it - first)};
|
||||
}
|
||||
|
||||
static
|
||||
boost::string_ref
|
||||
parse_path(char const*& it)
|
||||
{
|
||||
auto const first = it;
|
||||
while(is_pathchar(*it))
|
||||
++it;
|
||||
if(*it != ' ')
|
||||
return {};
|
||||
return {first, static_cast<
|
||||
boost::string_ref::size_type>(
|
||||
it - first)};
|
||||
}
|
||||
|
||||
static
|
||||
boost::string_ref
|
||||
parse_name(char const*& it)
|
||||
{
|
||||
auto const first = it;
|
||||
while(to_field_char(*it))
|
||||
++it;
|
||||
return {first, static_cast<
|
||||
boost::string_ref::size_type>(
|
||||
it - first)};
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
parse_version(char const*& it)
|
||||
{
|
||||
if(*it != 'H')
|
||||
return -1;
|
||||
if(*++it != 'T')
|
||||
return -1;
|
||||
if(*++it != 'T')
|
||||
return -1;
|
||||
if(*++it != 'P')
|
||||
return -1;
|
||||
if(*++it != '/')
|
||||
return -1;
|
||||
if(! is_digit(*++it))
|
||||
return -1;
|
||||
int v = 10 * (*it - '0');
|
||||
if(*++it != '.')
|
||||
return -1;
|
||||
if(! is_digit(*++it))
|
||||
return -1;
|
||||
v += *it++ - '0';
|
||||
return v;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
parse_status(char const*& it)
|
||||
{
|
||||
int v;
|
||||
if(! is_digit(*it))
|
||||
return -1;
|
||||
v = 100 * (*it - '0');
|
||||
if(! is_digit(*++it))
|
||||
return -1;
|
||||
v += 10 * (*it - '0');
|
||||
if(! is_digit(*++it))
|
||||
return -1;
|
||||
v += (*it++ - '0');
|
||||
return v;
|
||||
}
|
||||
|
||||
static
|
||||
boost::string_ref
|
||||
parse_reason(char const*& it)
|
||||
{
|
||||
auto const first = it;
|
||||
while(*it != '\r')
|
||||
{
|
||||
if(! is_text(*it))
|
||||
return {};
|
||||
++it;
|
||||
}
|
||||
return {first, static_cast<
|
||||
std::size_t>(it - first)};
|
||||
}
|
||||
|
||||
// VFALCO Can SIMD help this?
|
||||
static
|
||||
char const*
|
||||
find_eol(
|
||||
char const* first, char const* last,
|
||||
error_code& ec)
|
||||
{
|
||||
auto it = first;
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
return nullptr;
|
||||
if(*it == '\r')
|
||||
{
|
||||
if(++it == last)
|
||||
return nullptr;
|
||||
if(*it != '\n')
|
||||
{
|
||||
ec = error::bad_line_ending;
|
||||
return nullptr;
|
||||
}
|
||||
return ++it;
|
||||
}
|
||||
// VFALCO Should we handle the legacy case
|
||||
// for lines terminated with a single '\n'?
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// VFALCO Can SIMD help this?
|
||||
static
|
||||
char const*
|
||||
find_eom(
|
||||
char const* first, char const* last,
|
||||
error_code& ec)
|
||||
{
|
||||
auto it = first;
|
||||
for(;;)
|
||||
{
|
||||
if(it == last)
|
||||
return nullptr;
|
||||
if(*it == '\r')
|
||||
{
|
||||
if(++it == last)
|
||||
return nullptr;
|
||||
if(*it != '\n')
|
||||
{
|
||||
ec = error::bad_line_ending;
|
||||
return nullptr;
|
||||
}
|
||||
if(++it == last)
|
||||
return nullptr;
|
||||
if(*it != '\r')
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
if(++it == last)
|
||||
return nullptr;
|
||||
if(*it != '\n')
|
||||
{
|
||||
ec = error::bad_line_ending;
|
||||
return nullptr;
|
||||
}
|
||||
return ++it;
|
||||
}
|
||||
// VFALCO Should we handle the legacy case
|
||||
// for lines terminated with a single '\n'?
|
||||
++it;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,146 +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_DETAIL_BASIC_PARSER_V1_HPP
|
||||
#define BEAST_HTTP_DETAIL_BASIC_PARSER_V1_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<class = void>
|
||||
struct parser_str_t
|
||||
{
|
||||
static char constexpr close[6] = "close";
|
||||
static char constexpr chunked[8] = "chunked";
|
||||
static char constexpr keep_alive[11] = "keep-alive";
|
||||
|
||||
static char constexpr upgrade[8] = "upgrade";
|
||||
static char constexpr connection[11] = "connection";
|
||||
static char constexpr content_length[15] = "content-length";
|
||||
static char constexpr proxy_connection[17] = "proxy-connection";
|
||||
static char constexpr transfer_encoding[18] = "transfer-encoding";
|
||||
};
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::close[6];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::chunked[8];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::keep_alive[11];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::upgrade[8];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::connection[11];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::content_length[15];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::proxy_connection[17];
|
||||
|
||||
template<class _>
|
||||
char constexpr
|
||||
parser_str_t<_>::transfer_encoding[18];
|
||||
|
||||
using parser_str = parser_str_t<>;
|
||||
|
||||
class parser_base
|
||||
{
|
||||
protected:
|
||||
enum state : std::uint8_t
|
||||
{
|
||||
s_dead = 1,
|
||||
|
||||
s_req_start,
|
||||
s_req_method0,
|
||||
s_req_method,
|
||||
s_req_url0,
|
||||
s_req_url,
|
||||
s_req_http,
|
||||
s_req_http_H,
|
||||
s_req_http_HT,
|
||||
s_req_http_HTT,
|
||||
s_req_http_HTTP,
|
||||
s_req_major,
|
||||
s_req_dot,
|
||||
s_req_minor,
|
||||
s_req_cr,
|
||||
s_req_lf,
|
||||
|
||||
s_res_start,
|
||||
s_res_H,
|
||||
s_res_HT,
|
||||
s_res_HTT,
|
||||
s_res_HTTP,
|
||||
s_res_major,
|
||||
s_res_dot,
|
||||
s_res_minor,
|
||||
s_res_space_1,
|
||||
s_res_status0,
|
||||
s_res_status1,
|
||||
s_res_status2,
|
||||
s_res_space_2,
|
||||
s_res_reason0,
|
||||
s_res_reason,
|
||||
s_res_line_lf,
|
||||
s_res_line_done,
|
||||
|
||||
s_header_name0,
|
||||
s_header_name,
|
||||
s_header_value0_lf,
|
||||
s_header_value0_almost_done,
|
||||
s_header_value0,
|
||||
s_header_value,
|
||||
s_header_value_lf,
|
||||
s_header_value_almost_done,
|
||||
s_header_value_unfold,
|
||||
|
||||
s_headers_almost_done,
|
||||
s_headers_done,
|
||||
|
||||
s_chunk_size0,
|
||||
s_chunk_size,
|
||||
s_chunk_ext_name0,
|
||||
s_chunk_ext_name,
|
||||
s_chunk_ext_val,
|
||||
s_chunk_size_lf,
|
||||
s_chunk_data0,
|
||||
s_chunk_data,
|
||||
s_chunk_data_cr,
|
||||
s_chunk_data_lf,
|
||||
|
||||
s_body_pause,
|
||||
s_body_identity0,
|
||||
s_body_identity,
|
||||
s_body_identity_eof0,
|
||||
s_body_identity_eof,
|
||||
|
||||
s_complete,
|
||||
s_restart,
|
||||
s_closed_complete
|
||||
};
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -46,7 +46,7 @@ is_alpha(char c)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -73,7 +73,7 @@ is_text(char c)
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -105,7 +105,7 @@ is_tchar(char c)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -134,7 +134,7 @@ is_qdchar(char c)
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -164,7 +164,7 @@ is_qpchar(char c)
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
// converts to lower case,
|
||||
@@ -200,7 +200,7 @@ to_field_char(char c)
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
// converts to lower case,
|
||||
@@ -230,9 +230,10 @@ to_value_char(char c)
|
||||
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return static_cast<char>(tab[static_cast<std::uint8_t>(c)]);
|
||||
return static_cast<char>(tab[static_cast<unsigned char>(c)]);
|
||||
}
|
||||
|
||||
// VFALCO TODO Make this return unsigned?
|
||||
inline
|
||||
std::int8_t
|
||||
unhex(char c)
|
||||
@@ -256,22 +257,68 @@ unhex(char c)
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240
|
||||
};
|
||||
static_assert(sizeof(tab) == 256, "");
|
||||
return tab[static_cast<std::uint8_t>(c)];
|
||||
return tab[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
template<class FwdIt>
|
||||
inline
|
||||
void
|
||||
skip_ows(FwdIt& it, FwdIt const& end)
|
||||
{
|
||||
while(it != end)
|
||||
{
|
||||
auto const c = *it;
|
||||
if(c != ' ' && c != '\t')
|
||||
if(*it != ' ' && *it != '\t')
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
template<class RanIt>
|
||||
inline
|
||||
void
|
||||
skip_ows_rev(
|
||||
RanIt& it, RanIt const& first)
|
||||
{
|
||||
while(it != first)
|
||||
{
|
||||
auto const c = it[-1];
|
||||
if(c != ' ' && c != '\t')
|
||||
break;
|
||||
--it;
|
||||
}
|
||||
}
|
||||
|
||||
// obs-fold = CRLF 1*( SP / HTAB )
|
||||
// return `false` on parse error
|
||||
//
|
||||
template<class FwdIt>
|
||||
inline
|
||||
bool
|
||||
skip_obs_fold(
|
||||
FwdIt& it, FwdIt const& last)
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(*it != '\r')
|
||||
return true;
|
||||
if(++it == last)
|
||||
return false;
|
||||
if(*it != '\n')
|
||||
return false;
|
||||
if(++it == last)
|
||||
return false;
|
||||
if(*it != ' ' && *it != '\t')
|
||||
return false;
|
||||
for(;;)
|
||||
{
|
||||
if(++it == last)
|
||||
return true;
|
||||
if(*it != ' ' && *it != '\t')
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class FwdIt>
|
||||
void
|
||||
skip_token(FwdIt& it, FwdIt const& last)
|
||||
@@ -403,6 +450,53 @@ increment()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
#token = [ ( "," / token ) *( OWS "," [ OWS token ] ) ]
|
||||
*/
|
||||
struct opt_token_list_policy
|
||||
{
|
||||
using value_type = boost::string_ref;
|
||||
|
||||
bool
|
||||
operator()(value_type& v,
|
||||
char const*& it, boost::string_ref const& s) const
|
||||
{
|
||||
v = {};
|
||||
auto need_comma = it != s.begin();
|
||||
for(;;)
|
||||
{
|
||||
detail::skip_ows(it, s.end());
|
||||
if(it == s.end())
|
||||
{
|
||||
it = nullptr;
|
||||
return true;
|
||||
}
|
||||
auto const c = *it;
|
||||
if(detail::is_tchar(c))
|
||||
{
|
||||
if(need_comma)
|
||||
return false;
|
||||
auto const p0 = it;
|
||||
for(;;)
|
||||
{
|
||||
++it;
|
||||
if(it == s.end())
|
||||
break;
|
||||
if(! detail::is_tchar(*it))
|
||||
break;
|
||||
}
|
||||
v = boost::string_ref{&*p0,
|
||||
static_cast<std::size_t>(it - p0)};
|
||||
return true;
|
||||
}
|
||||
if(c != ',')
|
||||
return false;
|
||||
need_comma = false;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// 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_ERROR_HPP
|
||||
#define BEAST_HTTP_ERROR_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/// Error codes returned from HTTP parsing
|
||||
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.
|
||||
*/
|
||||
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
|
||||
entire message.
|
||||
*/
|
||||
partial_message,
|
||||
|
||||
/** Buffer maximum exceeded.
|
||||
|
||||
This error is returned when reading HTTP content
|
||||
into a dynamic buffer, and the operation would
|
||||
exceed the maximum size of the buffer.
|
||||
*/
|
||||
buffer_overflow,
|
||||
|
||||
/// The line ending was malformed
|
||||
bad_line_ending,
|
||||
|
||||
/// The method is invalid.
|
||||
bad_method,
|
||||
|
||||
/// The request-target is invalid.
|
||||
bad_path,
|
||||
|
||||
/// The HTTP-version is invalid.
|
||||
bad_version,
|
||||
|
||||
/// The status-code is invalid.
|
||||
bad_status,
|
||||
|
||||
/// The reason-phrase is invalid.
|
||||
bad_reason,
|
||||
|
||||
/// The field name is invalid.
|
||||
bad_field,
|
||||
|
||||
/// The field value is invalid.
|
||||
bad_value,
|
||||
|
||||
/// The Content-Length is invalid.
|
||||
bad_content_length,
|
||||
|
||||
/// The Transfer-Encoding is invalid.
|
||||
bad_transfer_encoding,
|
||||
|
||||
/// The chunk syntax is invalid.
|
||||
bad_chunk
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/error.ipp>
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,190 @@
|
||||
//
|
||||
// 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_HEADER_PARSER_HPP
|
||||
#define BEAST_HTTP_HEADER_PARSER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/basic_parser.hpp>
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A parser for producing HTTP/1 headers.
|
||||
|
||||
This class uses the basic HTTP/1 wire format parser to convert
|
||||
a series of octets into a @ref header.
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
|
||||
@tparam isRequest Indicates whether a request or response
|
||||
will be parsed.
|
||||
|
||||
@tparam Fields The type of container used to represent the fields.
|
||||
*/
|
||||
template<bool isRequest, class Fields>
|
||||
class header_parser
|
||||
: public basic_parser<isRequest, false,
|
||||
header_parser<isRequest, Fields>>
|
||||
{
|
||||
header<isRequest, Fields> h_;
|
||||
|
||||
public:
|
||||
using mutable_buffers_type =
|
||||
boost::asio::null_buffers;
|
||||
|
||||
/// The type of @ref header this object produces.
|
||||
using value_type = header<isRequest, Fields>;
|
||||
|
||||
/// Copy constructor.
|
||||
header_parser(header_parser const&) = default;
|
||||
|
||||
/// Copy assignment.
|
||||
header_parser& operator=(header_parser const&) = default;
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
After the move, the only valid operation
|
||||
on the moved-from object is destruction.
|
||||
*/
|
||||
header_parser(header_parser&&) = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param args If present, additional arguments to be
|
||||
forwarded to the @ref beast::http::header constructor.
|
||||
*/
|
||||
template<class... Args>
|
||||
explicit
|
||||
header_parser(Args&&... args);
|
||||
|
||||
/** Returns the parsed header
|
||||
|
||||
Only valid if @ref got_header would return `true`.
|
||||
*/
|
||||
value_type const&
|
||||
get() const
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Returns the parsed header.
|
||||
|
||||
Only valid if @ref got_header would return `true`.
|
||||
*/
|
||||
value_type&
|
||||
get()
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Returns ownership of the parsed header.
|
||||
|
||||
Ownership is transferred to the caller. Only
|
||||
valid if @ref got_header would return `true`.
|
||||
|
||||
Requires:
|
||||
@ref value_type is @b MoveConstructible
|
||||
*/
|
||||
value_type
|
||||
release()
|
||||
{
|
||||
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>;
|
||||
|
||||
void
|
||||
on_request(
|
||||
boost::string_ref const& method,
|
||||
boost::string_ref const& path,
|
||||
int version, error_code&)
|
||||
{
|
||||
h_.url = std::string{
|
||||
path.data(), path.size()};
|
||||
h_.method = std::string{
|
||||
method.data(), method.size()};
|
||||
h_.version = version;
|
||||
}
|
||||
|
||||
void
|
||||
on_response(int status,
|
||||
boost::string_ref const& reason,
|
||||
int version, error_code&)
|
||||
{
|
||||
h_.status = status;
|
||||
h_.reason = std::string{
|
||||
reason.data(), reason.size()};
|
||||
h_.version = version;
|
||||
}
|
||||
|
||||
void
|
||||
on_field(boost::string_ref const& name,
|
||||
boost::string_ref const& value,
|
||||
error_code&)
|
||||
{
|
||||
h_.fields.insert(name, value);
|
||||
}
|
||||
|
||||
void
|
||||
on_header(error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_body(error_code& ec)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_body(std::uint64_t content_length,
|
||||
error_code& ec)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_data(boost::string_ref const& s,
|
||||
error_code& ec)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_commit(std::size_t n)
|
||||
{
|
||||
// Can't write body data with header-only parser!
|
||||
BOOST_ASSERT(false);
|
||||
throw std::logic_error{
|
||||
"invalid member function call"};
|
||||
}
|
||||
|
||||
void
|
||||
on_chunk(std::uint64_t n,
|
||||
boost::string_ref const& ext,
|
||||
error_code& ec)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_complete(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/header_parser.ipp>
|
||||
|
||||
#endif
|
||||
@@ -1,233 +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_HEADERS_PARSER_V1_HPP
|
||||
#define BEAST_HTTP_HEADERS_PARSER_V1_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/basic_parser_v1.hpp>
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct request_parser_base
|
||||
{
|
||||
std::string method_;
|
||||
std::string uri_;
|
||||
};
|
||||
|
||||
struct response_parser_base
|
||||
{
|
||||
std::string reason_;
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/** A parser for a HTTP/1 request or response header.
|
||||
|
||||
This class uses the HTTP/1 wire format parser to
|
||||
convert a series of octets into a request or
|
||||
response @ref header.
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
*/
|
||||
template<bool isRequest, class Fields>
|
||||
class header_parser_v1
|
||||
: public basic_parser_v1<isRequest,
|
||||
header_parser_v1<isRequest, Fields>>
|
||||
, private std::conditional<isRequest,
|
||||
detail::request_parser_base,
|
||||
detail::response_parser_base>::type
|
||||
{
|
||||
public:
|
||||
/// The type of the header this parser produces.
|
||||
using header_type = header<isRequest, Fields>;
|
||||
|
||||
private:
|
||||
// VFALCO Check Fields requirements?
|
||||
|
||||
std::string field_;
|
||||
std::string value_;
|
||||
header_type h_;
|
||||
bool flush_ = false;
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
header_parser_v1() = default;
|
||||
|
||||
/// Move constructor
|
||||
header_parser_v1(header_parser_v1&&) = default;
|
||||
|
||||
/// Copy constructor (disallowed)
|
||||
header_parser_v1(header_parser_v1 const&) = delete;
|
||||
|
||||
/// Move assignment (disallowed)
|
||||
header_parser_v1& operator=(header_parser_v1&&) = delete;
|
||||
|
||||
/// Copy assignment (disallowed)
|
||||
header_parser_v1& operator=(header_parser_v1 const&) = delete;
|
||||
|
||||
/** Construct the parser.
|
||||
|
||||
@param args Forwarded to the header constructor.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class... Args>
|
||||
explicit
|
||||
header_parser_v1(Args&&... args);
|
||||
#else
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<! std::is_same<
|
||||
typename std::decay<Arg1>::type, header_parser_v1>::value>>
|
||||
explicit
|
||||
header_parser_v1(Arg1&& arg1, ArgN&&... argn)
|
||||
: h_(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Returns the parsed header
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
header_type const&
|
||||
get() const
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Returns the parsed header.
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
header_type&
|
||||
get()
|
||||
{
|
||||
return h_;
|
||||
}
|
||||
|
||||
/** Returns ownership of the parsed header.
|
||||
|
||||
Ownership is transferred to the caller. Only
|
||||
valid if @ref complete would return `true`.
|
||||
|
||||
Requires:
|
||||
@ref header_type is @b MoveConstructible
|
||||
*/
|
||||
header_type
|
||||
release()
|
||||
{
|
||||
static_assert(std::is_move_constructible<decltype(h_)>::value,
|
||||
"MoveConstructible requirements not met");
|
||||
return std::move(h_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parser_v1<isRequest, header_parser_v1>;
|
||||
|
||||
void flush()
|
||||
{
|
||||
if(! flush_)
|
||||
return;
|
||||
flush_ = false;
|
||||
BOOST_ASSERT(! field_.empty());
|
||||
h_.fields.insert(field_, value_);
|
||||
field_.clear();
|
||||
value_.clear();
|
||||
}
|
||||
|
||||
void on_start(error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void on_method(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->method_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_uri(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->uri_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_reason(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->reason_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_request_or_response(std::true_type)
|
||||
{
|
||||
h_.method = std::move(this->method_);
|
||||
h_.url = std::move(this->uri_);
|
||||
}
|
||||
|
||||
void on_request_or_response(std::false_type)
|
||||
{
|
||||
h_.status = this->status_code();
|
||||
h_.reason = std::move(this->reason_);
|
||||
}
|
||||
|
||||
void on_request(error_code& ec)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_response(error_code& ec)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_field(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
flush();
|
||||
field_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_value(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
value_.append(s.data(), s.size());
|
||||
flush_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
on_header(std::uint64_t, error_code&)
|
||||
{
|
||||
flush();
|
||||
h_.version = 10 * this->http_major() + this->http_minor();
|
||||
}
|
||||
|
||||
body_what
|
||||
on_body_what(std::uint64_t, error_code&)
|
||||
{
|
||||
return body_what::pause;
|
||||
}
|
||||
|
||||
void on_body(boost::string_ref const&, error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void on_complete(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,716 @@
|
||||
//
|
||||
// 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/concepts.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_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/stream_concepts.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_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, db(db_)
|
||||
, p(p_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void
|
||||
asio_handler_deallocate(
|
||||
void* p, std::size_t size,
|
||||
read_some_buffer_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, 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)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, 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:
|
||||
{
|
||||
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())
|
||||
goto upcall;
|
||||
// caller sees EOF on next read.
|
||||
ec = {};
|
||||
d.p.write_eof(ec);
|
||||
if(ec)
|
||||
goto upcall;
|
||||
BOOST_ASSERT(d.p.is_complete());
|
||||
goto upcall;
|
||||
}
|
||||
else 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_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, db(db_)
|
||||
, p(p_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void
|
||||
asio_handler_deallocate(
|
||||
void* p, std::size_t size,
|
||||
read_some_body_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, 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)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, 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_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, db(db_)
|
||||
, p(p_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void
|
||||
asio_handler_deallocate(
|
||||
void* p, std::size_t size,
|
||||
parse_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, 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)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, 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_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, db(sb_)
|
||||
, m(m_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_message_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, 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)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, 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>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code, std::size_t)>::result_type
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, true, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
beast::async_completion<ReadHandler,
|
||||
void(error_code, std::size_t)> completion{handler};
|
||||
switch(parser.state())
|
||||
{
|
||||
case parse_state::header:
|
||||
case parse_state::chunk_header:
|
||||
detail::read_some_buffer_op<AsyncReadStream,
|
||||
DynamicBuffer, isRequest, true, Derived,
|
||||
decltype(completion.handler)>{
|
||||
completion.handler, stream, dynabuf, parser};
|
||||
break;
|
||||
|
||||
default:
|
||||
detail::read_some_body_op<AsyncReadStream,
|
||||
DynamicBuffer, isRequest, Derived,
|
||||
decltype(completion.handler)>{
|
||||
completion.handler, stream, dynabuf, parser};
|
||||
break;
|
||||
}
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
inline
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code, std::size_t)>::result_type
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, false, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
beast::async_completion<ReadHandler,
|
||||
void(error_code, std::size_t)> completion{handler};
|
||||
detail::read_some_buffer_op<AsyncReadStream,
|
||||
DynamicBuffer, isRequest, false, Derived,
|
||||
decltype(completion.handler)>{
|
||||
completion.handler, stream, dynabuf, parser};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code, std::size_t)>::result_type
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
return detail::async_read_some(stream, dynabuf, parser,
|
||||
std::forward<ReadHandler>(handler));
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
beast::async_completion<ReadHandler,
|
||||
void(error_code)> completion{handler};
|
||||
detail::parse_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, isDirect, Derived, decltype(completion.handler)>{
|
||||
completion.handler, stream, dynabuf, parser};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_Body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(has_reader<Body>::value,
|
||||
"Body has no reader");
|
||||
static_assert(is_Reader<typename Body::reader,
|
||||
message<isRequest, Body, Fields>>::value,
|
||||
"Reader requirements not met");
|
||||
beast::async_completion<ReadHandler,
|
||||
void(error_code)> completion{handler};
|
||||
detail::read_message_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Body, Fields, decltype(
|
||||
completion.handler)>{completion.handler,
|
||||
stream, dynabuf, msg};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_IMPL_ERROR_IPP
|
||||
#define BEAST_HTTP_IMPL_ERROR_IPP
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
template<>
|
||||
struct is_error_code_enum<beast::http::error>
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // system
|
||||
} // boost
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
class http_error_category : public error_category
|
||||
{
|
||||
public:
|
||||
const char*
|
||||
name() const noexcept override
|
||||
{
|
||||
return "http";
|
||||
}
|
||||
|
||||
std::string
|
||||
message(int ev) const override
|
||||
{
|
||||
switch(static_cast<error>(ev))
|
||||
{
|
||||
default:
|
||||
case error::end_of_stream: return "end of stream";
|
||||
case error::partial_message: return "partial message";
|
||||
case error::buffer_overflow: return "buffer overflow";
|
||||
case error::bad_line_ending: return "bad line ending";
|
||||
case error::bad_method: return "bad method";
|
||||
case error::bad_path: return "bad path";
|
||||
case error::bad_version: return "bad version";
|
||||
case error::bad_status: return "bad status";
|
||||
case error::bad_reason: return "bad reason";
|
||||
case error::bad_field: return "bad field";
|
||||
case error::bad_value: return "bad value";
|
||||
case error::bad_content_length: return "bad Content-Length";
|
||||
case error::bad_transfer_encoding: return "bad Transfer-Encoding";
|
||||
case error::bad_chunk: return "bad chunk";
|
||||
}
|
||||
}
|
||||
|
||||
error_condition
|
||||
default_error_condition(
|
||||
int ev) const noexcept override
|
||||
{
|
||||
return error_condition{ev, *this};
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(int ev,
|
||||
error_condition const& condition
|
||||
) const noexcept override
|
||||
{
|
||||
return condition.value() == ev &&
|
||||
&condition.category() == this;
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(error_code const& error,
|
||||
int ev) const noexcept override
|
||||
{
|
||||
return error.value() == ev &&
|
||||
&error.category() == this;
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
error_category const&
|
||||
get_http_error_category()
|
||||
{
|
||||
static http_error_category const cat{};
|
||||
return cat;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
error_code
|
||||
make_error_code(error ev)
|
||||
{
|
||||
return error_code{
|
||||
static_cast<std::underlying_type<error>::type>(ev),
|
||||
detail::get_http_error_category()};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// 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_HEADER_PARSER_IPP
|
||||
#define BEAST_HTTP_IMPL_HEADER_PARSER_IPP
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
template<class... Args>
|
||||
header_parser<isRequest, Fields>::
|
||||
header_parser(Args&&... args)
|
||||
: h_(std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// 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_MESSAGE_PARSER_IPP
|
||||
#define BEAST_HTTP_IMPL_MESSAGE_PARSER_IPP
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class Arg1, class... ArgN, class>
|
||||
message_parser<isRequest, Body, Fields>::
|
||||
message_parser(Arg1&& arg1, ArgN&&... argn)
|
||||
: m_(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<class... Args>
|
||||
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)))
|
||||
, m_(parser.release(), std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,302 +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_PARSE_IPP_HPP
|
||||
#define BEAST_HTTP_IMPL_PARSE_IPP_HPP
|
||||
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Stream,
|
||||
class DynamicBuffer, class Parser, class Handler>
|
||||
class parse_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
bool cont;
|
||||
Stream& s;
|
||||
DynamicBuffer& db;
|
||||
Parser& p;
|
||||
bool got_some = false;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, Stream& s_,
|
||||
DynamicBuffer& sb_, Parser& p_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, db(sb_)
|
||||
, p(p_)
|
||||
{
|
||||
BOOST_ASSERT(! p.complete());
|
||||
}
|
||||
};
|
||||
|
||||
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 ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, parse_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, parse_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, 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)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream,
|
||||
class DynamicBuffer, class Parser, class Handler>
|
||||
void
|
||||
parse_op<Stream, DynamicBuffer, Parser, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_transferred, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// Parse any bytes left over in the buffer
|
||||
auto const used =
|
||||
d.p.write(d.db.data(), ec);
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.s.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
return;
|
||||
}
|
||||
if(used > 0)
|
||||
{
|
||||
d.got_some = true;
|
||||
d.db.consume(used);
|
||||
}
|
||||
if(d.p.complete())
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.s.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
return;
|
||||
}
|
||||
// Buffer must be empty,
|
||||
// otherwise parse should be complete
|
||||
BOOST_ASSERT(d.db.size() == 0);
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
// read
|
||||
d.state = 2;
|
||||
auto const size =
|
||||
read_size_helper(d.db, 65536);
|
||||
BOOST_ASSERT(size > 0);
|
||||
d.s.async_read_some(
|
||||
d.db.prepare(size), std::move(*this));
|
||||
return;
|
||||
}
|
||||
|
||||
// got data
|
||||
case 2:
|
||||
{
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
// If we haven't processed any bytes,
|
||||
// give the eof to the handler immediately.
|
||||
if(! d.got_some)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
// Feed the eof to the parser to complete
|
||||
// the parse, and call the handler. The
|
||||
// next call to parse will deliver the eof.
|
||||
ec = {};
|
||||
d.p.write_eof(ec);
|
||||
BOOST_ASSERT(ec || d.p.complete());
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
BOOST_ASSERT(bytes_transferred > 0);
|
||||
d.db.commit(bytes_transferred);
|
||||
auto const used = d.p.write(d.db.data(), ec);
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
// The parser must either consume
|
||||
// bytes or generate an error.
|
||||
BOOST_ASSERT(used > 0);
|
||||
d.got_some = true;
|
||||
d.db.consume(used);
|
||||
if(d.p.complete())
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
// If the parse is not complete,
|
||||
// all input must be consumed.
|
||||
BOOST_ASSERT(used == bytes_transferred);
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class SyncReadStream, class DynamicBuffer, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf, Parser& parser)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_Parser<Parser>::value,
|
||||
"Parser requirements not met");
|
||||
error_code ec;
|
||||
parse(stream, dynabuf, parser, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class SyncReadStream, class DynamicBuffer, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
Parser& parser, error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_Parser<Parser>::value,
|
||||
"Parser requirements not met");
|
||||
bool got_some = false;
|
||||
for(;;)
|
||||
{
|
||||
auto used =
|
||||
parser.write(dynabuf.data(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
dynabuf.consume(used);
|
||||
if(used > 0)
|
||||
got_some = true;
|
||||
if(parser.complete())
|
||||
break;
|
||||
dynabuf.commit(stream.read_some(
|
||||
dynabuf.prepare(read_size_helper(
|
||||
dynabuf, 65536)), ec));
|
||||
if(ec && ec != boost::asio::error::eof)
|
||||
return;
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
if(! got_some)
|
||||
return;
|
||||
// Caller will see eof on next read.
|
||||
ec = {};
|
||||
parser.write_eof(ec);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(parser.complete());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class AsyncReadStream,
|
||||
class DynamicBuffer, class Parser, class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
async_parse(AsyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf, Parser& parser, ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_Parser<Parser>::value,
|
||||
"Parser requirements not met");
|
||||
beast::async_completion<ReadHandler,
|
||||
void(error_code)> completion{handler};
|
||||
detail::parse_op<AsyncReadStream, DynamicBuffer,
|
||||
Parser, decltype(completion.handler)>{
|
||||
completion.handler, stream, dynabuf, parser};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
@@ -1,105 +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_PARSE_ERROR_IPP
|
||||
#define BEAST_HTTP_IMPL_PARSE_ERROR_IPP
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
template<>
|
||||
struct is_error_code_enum<beast::http::parse_error>
|
||||
{
|
||||
static bool const value = true;
|
||||
};
|
||||
} // system
|
||||
} // boost
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
class parse_error_category : public error_category
|
||||
{
|
||||
public:
|
||||
const char*
|
||||
name() const noexcept override
|
||||
{
|
||||
return "http";
|
||||
}
|
||||
|
||||
std::string
|
||||
message(int ev) const override
|
||||
{
|
||||
switch(static_cast<parse_error>(ev))
|
||||
{
|
||||
case parse_error::connection_closed: return "data after Connection close";
|
||||
case parse_error::bad_method: return "bad method";
|
||||
case parse_error::bad_uri: return "bad request-target";
|
||||
case parse_error::bad_version: return "bad HTTP-Version";
|
||||
case parse_error::bad_crlf: return "missing CRLF";
|
||||
case parse_error::bad_status: return "bad status-code";
|
||||
case parse_error::bad_reason: return "bad reason-phrase";
|
||||
case parse_error::bad_field: return "bad field token";
|
||||
case parse_error::bad_value: return "bad field-value";
|
||||
case parse_error::bad_content_length: return "bad Content-Length";
|
||||
case parse_error::illegal_content_length: return "illegal Content-Length with chunked Transfer-Encoding";
|
||||
case parse_error::invalid_chunk_size: return "invalid chunk size";
|
||||
case parse_error::invalid_ext_name: return "invalid ext name";
|
||||
case parse_error::invalid_ext_val: return "invalid ext val";
|
||||
case parse_error::header_too_big: return "header size limit exceeded";
|
||||
case parse_error::body_too_big: return "body size limit exceeded";
|
||||
default:
|
||||
case parse_error::short_read: return "unexpected end of data";
|
||||
}
|
||||
}
|
||||
|
||||
error_condition
|
||||
default_error_condition(int ev) const noexcept override
|
||||
{
|
||||
return error_condition{ev, *this};
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(int ev,
|
||||
error_condition const& condition
|
||||
) const noexcept override
|
||||
{
|
||||
return condition.value() == ev &&
|
||||
&condition.category() == this;
|
||||
}
|
||||
|
||||
bool
|
||||
equivalent(error_code const& error, int ev) const noexcept override
|
||||
{
|
||||
return error.value() == ev &&
|
||||
&error.category() == this;
|
||||
}
|
||||
};
|
||||
|
||||
inline
|
||||
error_category const&
|
||||
get_parse_error_category()
|
||||
{
|
||||
static parse_error_category const cat{};
|
||||
return cat;
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
inline
|
||||
error_code
|
||||
make_error_code(parse_error ev)
|
||||
{
|
||||
return error_code{
|
||||
static_cast<std::underlying_type<parse_error>::type>(ev),
|
||||
detail::get_parse_error_category()};
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
+234
-291
@@ -9,306 +9,275 @@
|
||||
#define BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/header_parser_v1.hpp>
|
||||
#include <beast/http/parse.hpp>
|
||||
#include <beast/http/parser_v1.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_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Fields,
|
||||
class Handler>
|
||||
class read_header_op
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
inline
|
||||
std::size_t
|
||||
read_some_buffer(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
using parser_type =
|
||||
header_parser_v1<isRequest, Fields>;
|
||||
|
||||
using message_type =
|
||||
header<isRequest, Fields>;
|
||||
|
||||
struct data
|
||||
std::size_t bytes_used;
|
||||
if(dynabuf.size() == 0)
|
||||
goto do_read;
|
||||
for(;;)
|
||||
{
|
||||
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_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, db(sb_)
|
||||
, m(m_)
|
||||
bytes_used = parser.write(
|
||||
dynabuf.data(), ec);
|
||||
if(ec)
|
||||
return 0;
|
||||
if(bytes_used > 0)
|
||||
goto do_finish;
|
||||
do_read:
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> mb;
|
||||
auto const size =
|
||||
read_size_helper(dynabuf, 65536);
|
||||
BOOST_ASSERT(size > 0);
|
||||
try
|
||||
{
|
||||
mb.emplace(dynabuf.prepare(size));
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
read_header_op(read_header_op&&) = default;
|
||||
read_header_op(read_header_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_header_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_header_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_header_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_header_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_header_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Fields,
|
||||
class Handler>
|
||||
void
|
||||
read_header_op<Stream, DynamicBuffer, isRequest, Fields, Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
async_parse(d.s, d.db, d.p, std::move(*this));
|
||||
return;
|
||||
|
||||
case 1:
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.m = d.p.release();
|
||||
ec = error::buffer_overflow;
|
||||
return 0;
|
||||
}
|
||||
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;
|
||||
}
|
||||
else if(ec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
BOOST_ASSERT(bytes_transferred > 0);
|
||||
dynabuf.commit(bytes_transferred);
|
||||
}
|
||||
d_.invoke(ec);
|
||||
do_finish:
|
||||
return bytes_used;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
inline
|
||||
std::size_t
|
||||
read_some_body(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, true, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
if(dynabuf.size() > 0)
|
||||
return parser.copy_body(dynabuf);
|
||||
boost::optional<typename
|
||||
Derived::mutable_buffers_type> mb;
|
||||
try
|
||||
{
|
||||
parser.prepare_body(mb, 65536);
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return 0;
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
stream.read_some(*mb, ec);
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
// caller sees EOF on next read
|
||||
ec = {};
|
||||
parser.write_eof(ec);
|
||||
if(ec)
|
||||
return 0;
|
||||
BOOST_ASSERT(parser.is_complete());
|
||||
}
|
||||
else if(! ec)
|
||||
{
|
||||
parser.commit_body(bytes_transferred);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
inline
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, true, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
switch(parser.state())
|
||||
{
|
||||
case parse_state::header:
|
||||
case parse_state::chunk_header:
|
||||
return detail::read_some_buffer(
|
||||
stream, dynabuf, parser, ec);
|
||||
|
||||
default:
|
||||
return detail::read_some_body(
|
||||
stream, dynabuf, parser, ec);
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
inline
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, false, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
return detail::read_some_buffer(
|
||||
stream, dynabuf, parser, ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
header<isRequest, Fields>& msg)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
error_code ec;
|
||||
beast::http::read(stream, dynabuf, msg, ec);
|
||||
auto const bytes_used =
|
||||
read_some(stream, dynabuf, parser, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
return bytes_used;
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
return detail::read_some(stream, dynabuf, parser, ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
error_code ec;
|
||||
read(stream, dynabuf, parser, ec);
|
||||
if(ec)
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Fields>
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
header<isRequest, Fields>& m,
|
||||
error_code& ec)
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
header_parser_v1<isRequest, Fields> p;
|
||||
beast::http::parse(stream, dynabuf, p, ec);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(p.complete());
|
||||
m = p.release();
|
||||
}
|
||||
|
||||
template<class AsyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Fields,
|
||||
class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
header<isRequest, Fields>& m,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
beast::async_completion<ReadHandler,
|
||||
void(error_code)> completion{handler};
|
||||
detail::read_header_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Fields, decltype(
|
||||
completion.handler)>{completion.handler,
|
||||
stream, dynabuf, m};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Handler>
|
||||
class read_op
|
||||
{
|
||||
using parser_type =
|
||||
parser_v1<isRequest, Body, Fields>;
|
||||
|
||||
using message_type =
|
||||
message<isRequest, Body, Fields>;
|
||||
|
||||
struct data
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
do
|
||||
{
|
||||
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_)
|
||||
: cont(beast_asio_helpers::
|
||||
is_continuation(handler))
|
||||
, s(s_)
|
||||
, db(sb_)
|
||||
, m(m_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
read_op(read_op&&) = default;
|
||||
read_op(read_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_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_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
allocate(size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
deallocate(p, size, op->d_.handler());
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_op* op)
|
||||
{
|
||||
return beast_asio_helpers::
|
||||
invoke(f, op->d_.handler());
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Handler>
|
||||
void
|
||||
read_op<Stream, DynamicBuffer, isRequest, Body, Fields, Handler>::
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
async_parse(d.s, d.db, d.p, std::move(*this));
|
||||
auto const bytes_used =
|
||||
read_some(stream, dynabuf, parser, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
case 1:
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.m = d.p.release();
|
||||
break;
|
||||
}
|
||||
dynabuf.consume(bytes_used);
|
||||
}
|
||||
d_.invoke(ec);
|
||||
while(! parser.is_complete());
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& msg)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
@@ -328,12 +297,16 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
throw system_error{ec};
|
||||
}
|
||||
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& m,
|
||||
error_code& ec)
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
@@ -346,41 +319,11 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
static_assert(is_Reader<typename Body::reader,
|
||||
message<isRequest, Body, Fields>>::value,
|
||||
"Reader requirements not met");
|
||||
parser_v1<isRequest, Body, Fields> p;
|
||||
beast::http::parse(stream, dynabuf, p, ec);
|
||||
message_parser<isRequest, Body, Fields> p;
|
||||
beast::http::read(stream, dynabuf, p, ec);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(p.complete());
|
||||
m = p.release();
|
||||
}
|
||||
|
||||
template<class AsyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& m,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_Body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(has_reader<Body>::value,
|
||||
"Body has no reader");
|
||||
static_assert(is_Reader<typename Body::reader,
|
||||
message<isRequest, Body, Fields>>::value,
|
||||
"Reader requirements not met");
|
||||
beast::async_completion<ReadHandler,
|
||||
void(error_code)> completion{handler};
|
||||
detail::read_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Body, Fields, decltype(
|
||||
completion.handler)>{completion.handler,
|
||||
stream, dynabuf, m};
|
||||
return completion.result.get();
|
||||
msg = p.release();
|
||||
}
|
||||
|
||||
} // http
|
||||
|
||||
@@ -542,6 +542,26 @@ exists(T const& s)
|
||||
) != end();
|
||||
}
|
||||
|
||||
template<class Policy>
|
||||
bool
|
||||
validate_list(detail::basic_parsed_list<
|
||||
Policy> const& list)
|
||||
{
|
||||
auto const last = list.end();
|
||||
auto it = list.begin();
|
||||
if(it.error())
|
||||
return false;
|
||||
while(it != last)
|
||||
{
|
||||
++it;
|
||||
if(it.error())
|
||||
return false;
|
||||
if(it == last)
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
//
|
||||
// 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_MESSAGE_PARSER_HPP
|
||||
#define BEAST_HTTP_MESSAGE_PARSER_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/header_parser.hpp>
|
||||
#include <beast/core/detail/clamp.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** A parser for producing HTTP/1 messages.
|
||||
|
||||
This class uses the basic HTTP/1 wire format parser to convert
|
||||
a series of octets into a @ref message.
|
||||
|
||||
@tparam isRequest Indicates whether a request or response
|
||||
will be parsed.
|
||||
|
||||
@tparam Body The type used to represent the body.
|
||||
|
||||
@tparam Fields The type of container used to represent the fields.
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
class message_parser
|
||||
: public basic_parser<isRequest,
|
||||
Body::reader::is_direct,
|
||||
message_parser<isRequest, Body, Fields>>
|
||||
{
|
||||
using base_type = basic_parser<isRequest, true,
|
||||
message_parser<isRequest, Body, Fields>>;
|
||||
|
||||
using reader_type = typename Body::reader;
|
||||
|
||||
message<isRequest, Body, Fields> m_;
|
||||
boost::optional<typename Body::reader> r_;
|
||||
|
||||
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 reader_type::mutable_buffers_type;
|
||||
|
||||
/// Constructor (default)
|
||||
message_parser() = default;
|
||||
|
||||
/// Copy constructor (disallowed)
|
||||
message_parser(message_parser const&) = delete;
|
||||
|
||||
/// Copy assignment (disallowed)
|
||||
message_parser& operator=(message_parser const&) = delete;
|
||||
|
||||
/** Move constructor.
|
||||
|
||||
After the move, the only valid operation
|
||||
on the moved-from object is destruction.
|
||||
*/
|
||||
message_parser(message_parser&& other);
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param args Optional arguments forwarded to the
|
||||
@ref http::header constructor.
|
||||
|
||||
@note This function participates in overload
|
||||
resolution only if the first argument is not a
|
||||
@ref http::header_parser or @ref message_parser.
|
||||
*/
|
||||
#if GENERATING_DOCS
|
||||
template<class... Args>
|
||||
explicit
|
||||
msesage_parser(Args&&... args);
|
||||
#else
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<
|
||||
! std::is_same<typename
|
||||
std::decay<Arg1>::type,
|
||||
header_parser<isRequest, Fields>>::value &&
|
||||
! std::is_same<typename
|
||||
std::decay<Arg1>::type, message_parser>::value
|
||||
>::type>
|
||||
explicit
|
||||
message_parser(Arg1&& arg1, ArgN&&... argn);
|
||||
#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.
|
||||
*/
|
||||
template<class... Args>
|
||||
explicit
|
||||
message_parser(header_parser<
|
||||
isRequest, Fields>&& parser, Args&&... args);
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Depending on the progress of the parser, portions
|
||||
of this object may be incomplete.
|
||||
*/
|
||||
value_type const&
|
||||
get() const
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Depending on the progress of the parser, portions
|
||||
of this object may be incomplete.
|
||||
*/
|
||||
value_type&
|
||||
get()
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/** Returns ownership of the parsed message.
|
||||
|
||||
Ownership is transferred to the caller.
|
||||
Depending on the progress of the parser, portions
|
||||
of this object may be incomplete.
|
||||
|
||||
@par Requires
|
||||
|
||||
@ref value_type is @b MoveConstructible
|
||||
*/
|
||||
value_type
|
||||
release()
|
||||
{
|
||||
static_assert(std::is_move_constructible<decltype(m_)>::value,
|
||||
"MoveConstructible requirements not met");
|
||||
return std::move(m_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parser<
|
||||
isRequest, Body::reader::is_direct,
|
||||
message_parser>;
|
||||
|
||||
void
|
||||
on_request(
|
||||
boost::string_ref const& method,
|
||||
boost::string_ref const& path,
|
||||
int version, error_code&)
|
||||
{
|
||||
m_.url = std::string{
|
||||
path.data(), path.size()};
|
||||
m_.method = std::string{
|
||||
method.data(), method.size()};
|
||||
m_.version = version;
|
||||
}
|
||||
|
||||
void
|
||||
on_response(int status,
|
||||
boost::string_ref const& reason,
|
||||
int version, error_code&)
|
||||
{
|
||||
m_.status = status;
|
||||
m_.reason = std::string{
|
||||
reason.data(), reason.size()};
|
||||
m_.version = version;
|
||||
}
|
||||
|
||||
void
|
||||
on_field(boost::string_ref const& name,
|
||||
boost::string_ref const& value,
|
||||
error_code&)
|
||||
{
|
||||
m_.fields.insert(name, value);
|
||||
}
|
||||
|
||||
void
|
||||
on_header(error_code& ec)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_body()
|
||||
{
|
||||
r_.emplace(m_);
|
||||
r_->init();
|
||||
}
|
||||
|
||||
void
|
||||
on_body(std::uint64_t content_length)
|
||||
{
|
||||
r_.emplace(m_);
|
||||
r_->init(content_length);
|
||||
}
|
||||
|
||||
void
|
||||
on_body(error_code& ec)
|
||||
{
|
||||
r_.emplace(m_);
|
||||
r_->init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
on_body(std::uint64_t content_length,
|
||||
error_code& ec)
|
||||
{
|
||||
r_.emplace(m_);
|
||||
r_->init(content_length, ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
on_data(boost::string_ref const& s,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(! Body::reader::is_direct, "");
|
||||
r_->write(s, ec);
|
||||
}
|
||||
|
||||
mutable_buffers_type
|
||||
on_prepare(std::size_t n)
|
||||
{
|
||||
return r_->prepare(n);
|
||||
}
|
||||
|
||||
void
|
||||
on_commit(std::size_t n)
|
||||
{
|
||||
r_->commit(n);
|
||||
}
|
||||
|
||||
void
|
||||
on_chunk(std::uint64_t,
|
||||
boost::string_ref const&,
|
||||
error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_complete(error_code& ec)
|
||||
{
|
||||
if(r_)
|
||||
do_on_complete(ec,
|
||||
std::integral_constant<bool,
|
||||
Body::reader::is_direct>{});
|
||||
}
|
||||
|
||||
void
|
||||
do_on_complete(
|
||||
error_code& ec, std::true_type)
|
||||
{
|
||||
r_->finish();
|
||||
}
|
||||
|
||||
void
|
||||
do_on_complete(
|
||||
error_code& ec, std::false_type)
|
||||
{
|
||||
r_->finish(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/message_parser.ipp>
|
||||
|
||||
#endif
|
||||
@@ -1,155 +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_PARSE_HPP
|
||||
#define BEAST_HTTP_PARSE_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/async_completion.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Parse an object 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 are met:
|
||||
|
||||
@li The parser indicates that parsing is complete.
|
||||
|
||||
@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 stream
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@note This algorithm is generic, and not specific to HTTP
|
||||
messages. It is up to the parser to determine what predicate
|
||||
defines a complete operation.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser An object meeting the requirements of @b Parser
|
||||
which will receive the data.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf, Parser& parser);
|
||||
|
||||
/** Parse an object 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 are met:
|
||||
|
||||
@li The parser indicates that parsing is complete.
|
||||
|
||||
@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 stream
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@note This algorithm is generic, and not specific to HTTP
|
||||
messages. It is up to the parser to determine what predicate
|
||||
defines a complete operation.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser An object meeting the requirements of @b Parser
|
||||
which will receive the data.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf, Parser& parser, error_code& ec);
|
||||
|
||||
/** Start an asynchronous operation to parse an object 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 parsing is complete.
|
||||
|
||||
@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.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b AsyncReadStream concept.
|
||||
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser An object meeting the requirements of @b Parser
|
||||
which will receive the data. This object must remain valid
|
||||
until the completion handler is invoked.
|
||||
|
||||
@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`.
|
||||
*/
|
||||
template<class AsyncReadStream,
|
||||
class DynamicBuffer, class Parser, class ReadHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_parse(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
Parser& parser, ReadHandler&& handler);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/parse.ipp>
|
||||
|
||||
#endif
|
||||
@@ -1,48 +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_PARSE_ERROR_HPP
|
||||
#define BEAST_HTTP_PARSE_ERROR_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
enum class parse_error
|
||||
{
|
||||
connection_closed = 1,
|
||||
|
||||
bad_method,
|
||||
bad_uri,
|
||||
bad_version,
|
||||
bad_crlf,
|
||||
|
||||
bad_status,
|
||||
bad_reason,
|
||||
|
||||
bad_field,
|
||||
bad_value,
|
||||
bad_content_length,
|
||||
illegal_content_length,
|
||||
|
||||
invalid_chunk_size,
|
||||
invalid_ext_name,
|
||||
invalid_ext_val,
|
||||
|
||||
header_too_big,
|
||||
body_too_big,
|
||||
short_read
|
||||
};
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/parse_error.ipp>
|
||||
|
||||
#endif
|
||||
@@ -1,339 +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_PARSER_V1_HPP
|
||||
#define BEAST_HTTP_PARSER_V1_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/header_parser_v1.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Skip body option.
|
||||
|
||||
The options controls whether or not the parser expects to see a
|
||||
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 use the @ref skip_body option to
|
||||
inform the parser that no body is expected. The parser will consider
|
||||
the message complete after the header has been received.
|
||||
|
||||
Example:
|
||||
@code
|
||||
parser_v1<true, empty_body, fields> p;
|
||||
p.set_option(skip_body{true});
|
||||
@endcode
|
||||
|
||||
@note Objects of this type are passed to @ref parser_v1::set_option.
|
||||
*/
|
||||
struct skip_body
|
||||
{
|
||||
bool value;
|
||||
|
||||
explicit
|
||||
skip_body(bool v)
|
||||
: value(v)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** A parser for producing HTTP/1 messages.
|
||||
|
||||
This class uses the basic HTTP/1 wire format parser to convert
|
||||
a series of octets into a `message`.
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
class parser_v1
|
||||
: public basic_parser_v1<isRequest,
|
||||
parser_v1<isRequest, Body, Fields>>
|
||||
, private std::conditional<isRequest,
|
||||
detail::request_parser_base,
|
||||
detail::response_parser_base>::type
|
||||
{
|
||||
public:
|
||||
/// The type of message this parser produces.
|
||||
using message_type =
|
||||
message<isRequest, Body, Fields>;
|
||||
|
||||
private:
|
||||
using reader =
|
||||
typename message_type::body_type::reader;
|
||||
|
||||
static_assert(is_Body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(has_reader<Body>::value,
|
||||
"Body has no reader");
|
||||
static_assert(is_Reader<reader, message_type>::value,
|
||||
"Reader requirements not met");
|
||||
|
||||
std::string field_;
|
||||
std::string value_;
|
||||
message_type m_;
|
||||
boost::optional<reader> r_;
|
||||
std::uint8_t skip_body_ = 0;
|
||||
bool flush_ = false;
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
parser_v1() = default;
|
||||
|
||||
/// Move constructor
|
||||
parser_v1(parser_v1&&) = default;
|
||||
|
||||
/// Copy constructor (disallowed)
|
||||
parser_v1(parser_v1 const&) = delete;
|
||||
|
||||
/// Move assignment (disallowed)
|
||||
parser_v1& operator=(parser_v1&&) = delete;
|
||||
|
||||
/// Copy assignment (disallowed)
|
||||
parser_v1& operator=(parser_v1 const&) = delete;
|
||||
|
||||
/** Construct the parser.
|
||||
|
||||
@param args Forwarded to the message constructor.
|
||||
|
||||
@note This function participates in overload resolution only
|
||||
if the first argument is not a parser or fields parser.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class... Args>
|
||||
explicit
|
||||
parser_v1(Args&&... args);
|
||||
#else
|
||||
template<class Arg1, class... ArgN,
|
||||
class = typename std::enable_if<
|
||||
! std::is_same<typename
|
||||
std::decay<Arg1>::type,
|
||||
header_parser_v1<isRequest, Fields>>::value &&
|
||||
! std::is_same<typename
|
||||
std::decay<Arg1>::type, parser_v1>::value
|
||||
>::type>
|
||||
explicit
|
||||
parser_v1(Arg1&& arg1, ArgN&&... argn)
|
||||
: m_(std::forward<Arg1>(arg1),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Construct the parser from a fields parser.
|
||||
|
||||
@param parser The fields parser to construct from.
|
||||
@param args Forwarded to the message body constructor.
|
||||
*/
|
||||
template<class... Args>
|
||||
explicit
|
||||
parser_v1(header_parser_v1<isRequest, Fields>& parser,
|
||||
Args&&... args)
|
||||
: m_(parser.release(), std::forward<Args>(args)...)
|
||||
{
|
||||
static_cast<basic_parser_v1<
|
||||
isRequest, parser_v1<
|
||||
isRequest, Body, Fields>>&>(*this) = parser;
|
||||
}
|
||||
|
||||
/// Set the skip body option.
|
||||
void
|
||||
set_option(skip_body const& o)
|
||||
{
|
||||
skip_body_ = o.value ? 1 : 0;
|
||||
}
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
message_type const&
|
||||
get() const
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Only valid if @ref complete would return `true`.
|
||||
*/
|
||||
message_type&
|
||||
get()
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/** Returns ownership of the parsed message.
|
||||
|
||||
Ownership is transferred to the caller. Only
|
||||
valid if @ref complete would return `true`.
|
||||
|
||||
Requires:
|
||||
`message<isRequest, Body, Fields>` is @b MoveConstructible
|
||||
*/
|
||||
message_type
|
||||
release()
|
||||
{
|
||||
static_assert(std::is_move_constructible<decltype(m_)>::value,
|
||||
"MoveConstructible requirements not met");
|
||||
return std::move(m_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parser_v1<isRequest, parser_v1>;
|
||||
|
||||
void flush()
|
||||
{
|
||||
if(! flush_)
|
||||
return;
|
||||
flush_ = false;
|
||||
BOOST_ASSERT(! field_.empty());
|
||||
m_.fields.insert(field_, value_);
|
||||
field_.clear();
|
||||
value_.clear();
|
||||
}
|
||||
|
||||
void on_start(error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void on_method(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->method_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_uri(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->uri_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_reason(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->reason_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_request_or_response(std::true_type)
|
||||
{
|
||||
m_.method = std::move(this->method_);
|
||||
m_.url = std::move(this->uri_);
|
||||
}
|
||||
|
||||
void on_request_or_response(std::false_type)
|
||||
{
|
||||
m_.status = this->status_code();
|
||||
m_.reason = std::move(this->reason_);
|
||||
}
|
||||
|
||||
void on_request(error_code&)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_response(error_code&)
|
||||
{
|
||||
on_request_or_response(
|
||||
std::integral_constant<bool, isRequest>{});
|
||||
}
|
||||
|
||||
void on_field(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
flush();
|
||||
field_.append(s.data(), s.size());
|
||||
}
|
||||
|
||||
void on_value(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
value_.append(s.data(), s.size());
|
||||
flush_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
on_header(std::uint64_t, error_code&)
|
||||
{
|
||||
flush();
|
||||
m_.version = 10 * this->http_major() + this->http_minor();
|
||||
}
|
||||
|
||||
body_what
|
||||
on_body_what(std::uint64_t, error_code& ec)
|
||||
{
|
||||
if(skip_body_)
|
||||
return body_what::skip;
|
||||
r_.emplace(m_);
|
||||
r_->init(ec);
|
||||
return body_what::normal;
|
||||
}
|
||||
|
||||
void on_body(boost::string_ref const& s, error_code& ec)
|
||||
{
|
||||
r_->write(s.data(), s.size(), ec);
|
||||
}
|
||||
|
||||
void on_complete(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/** Create a new parser from a fields parser.
|
||||
|
||||
Associates a Body type with a fields parser, and returns
|
||||
a new parser which parses a complete message object
|
||||
containing the original message fields and a new body
|
||||
of the specified body type.
|
||||
|
||||
This function allows HTTP messages to be parsed in two stages.
|
||||
First, the fields are parsed and control is returned. Then,
|
||||
the caller can choose at run-time, the type of Body to
|
||||
associate with the message. And finally, complete the parse
|
||||
in a second call.
|
||||
|
||||
@param parser The fields parser to construct from. Ownership
|
||||
of the message fields in the fields parser is transferred
|
||||
as if by call to @ref header_parser_v1::release.
|
||||
|
||||
@param args Forwarded to the body constructor of the message
|
||||
in the new parser.
|
||||
|
||||
@return A parser for a message with the specified @b Body type.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
headers_parser<true, fields> ph;
|
||||
...
|
||||
auto p = with_body<string_body>(ph);
|
||||
...
|
||||
message<true, string_body, fields> m = p.release();
|
||||
@endcode
|
||||
*/
|
||||
template<class Body,
|
||||
bool isRequest, class Fields, class... Args>
|
||||
parser_v1<isRequest, Body, Fields>
|
||||
with_body(header_parser_v1<isRequest, Fields>& parser,
|
||||
Args&&... args)
|
||||
{
|
||||
return parser_v1<isRequest, Body, Fields>(
|
||||
parser, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
+273
-93
@@ -11,135 +11,298 @@
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/async_completion.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/basic_parser.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Read a HTTP/1 header from a stream.
|
||||
/** Read some HTTP/1 message data from a stream.
|
||||
|
||||
This function is used to synchronously read a header
|
||||
from a stream. The call blocks until one of the following
|
||||
conditions is true:
|
||||
This function synchronously advances the state of the
|
||||
parser using the provided dynamic buffer and reading
|
||||
from the input stream as needed. The call will block
|
||||
until one of the following conditions is true:
|
||||
|
||||
@li An entire header is read in.
|
||||
@li When expecting a message header, and the complete
|
||||
header is received.
|
||||
|
||||
@li When expecting a chunk header, and the complete
|
||||
chunk header is received.
|
||||
|
||||
@li When expecting body octets, one or more body octets
|
||||
are 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 message
|
||||
fields being parsed. This additional data is stored in the
|
||||
stream buffer, which may be used in subsequent calls.
|
||||
|
||||
If the message corresponding to the header being received
|
||||
contains a message body, it is the callers responsibility
|
||||
to cause the body to be read in before attempting to read
|
||||
the next message.
|
||||
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.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b `SyncReadStream` concept.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param dynabuf A @b `DynamicBuffer` holding additional bytes
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object used to store the header. Any contents
|
||||
will be overwritten. The type must support copy assignment
|
||||
or move assignment.
|
||||
@param parser The parser to use.
|
||||
|
||||
@return The number of bytes processed from the dynamic
|
||||
buffer. The caller should remove these bytes by calling
|
||||
`consume` on the dynamic buffer.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
header<isRequest, Fields>& msg);
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser);
|
||||
|
||||
/** Read a HTTP/1 header from a stream.
|
||||
/** Read some HTTP/1 message data from a stream.
|
||||
|
||||
This function is used to synchronously read a header
|
||||
from a stream. The call blocks until one of the following
|
||||
conditions is true:
|
||||
This function synchronously advances the state of the
|
||||
parser using the provided dynamic buffer and reading
|
||||
from the input stream as needed. The call will block
|
||||
until one of the following conditions is true:
|
||||
|
||||
@li An entire header is read in.
|
||||
@li When expecting a message header, and the complete
|
||||
header is received.
|
||||
|
||||
@li When expecting a chunk header, and the complete
|
||||
chunk header is received.
|
||||
|
||||
@li When expecting body octets, one or more body octets
|
||||
are 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 message
|
||||
fields being parsed. This additional data is stored in the
|
||||
stream buffer, which may be used in subsequent calls.
|
||||
|
||||
If the message corresponding to the header being received
|
||||
contains a message body, it is the callers responsibility
|
||||
to cause the body to be read in before attempting to read
|
||||
the next message.
|
||||
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.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b `SyncReadStream` concept.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param dynabuf A @b `DynamicBuffer` holding additional bytes
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object used to store the header. Any contents
|
||||
will be overwritten. The type must support copy assignment
|
||||
or move assignment.
|
||||
@param parser The parser to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of bytes processed from the dynamic
|
||||
buffer. The caller should remove these bytes by calling
|
||||
`consume` on the dynamic buffer.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Start an asynchronous operation to read some HTTP/1 message data from a stream.
|
||||
|
||||
This function asynchronously advances the state of the
|
||||
parser using the provided dynamic buffer and reading from
|
||||
the input stream as needed. The function call always
|
||||
returns immediately. The asynchronous operation will
|
||||
continue until one of the following conditions is true:
|
||||
|
||||
@li When expecting a message header, and the complete
|
||||
header is received.
|
||||
|
||||
@li When expecting a chunk header, and the complete
|
||||
chunk header is received.
|
||||
|
||||
@li When expecting body octets, one or more body octets
|
||||
are received.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to
|
||||
the next layer's `async_read_some` function, and is known as a
|
||||
<em>composed operation</em>. The program must ensure that the
|
||||
stream performs no other 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.
|
||||
|
||||
The completion handler will be called with the number of bytes
|
||||
processed from the dynamic buffer. The caller should remove
|
||||
these bytes by calling `consume` on the dynamic buffer.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b AsyncReadStream concept.
|
||||
|
||||
@param dynabuf 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
|
||||
std::size_t bytes_used // the number of bytes to consume
|
||||
); @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`.
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
class ReadHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code, std::size_t)>::result_type
|
||||
#endif
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
ReadHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read an HTTP/1 message 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 no more additional data is needed.
|
||||
|
||||
@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.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param dynabuf 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.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser);
|
||||
|
||||
/** Read an HTTP/1 message 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 no more data is needed.
|
||||
|
||||
@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.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param dynabuf 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.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Fields>
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
header<isRequest, Fields>& msg,
|
||||
error_code& ec);
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Read a HTTP/1 header asynchronously from a stream.
|
||||
/** Start an asynchronous operation to read an HTTP/1 message from a stream.
|
||||
|
||||
This function is used to asynchronously read a header from
|
||||
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 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 An entire header is read in.
|
||||
@li The parser indicates that no more data is needed.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This operation is implemented in terms of one or more calls to
|
||||
the stream's `async_read_some` function, and is known as a
|
||||
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 message fields being parsed. This additional data is
|
||||
stored in the stream buffer, which may be used in subsequent calls.
|
||||
end of the object being parsed. This additional data is stored
|
||||
in the stream buffer, which may be used in subsequent calls.
|
||||
|
||||
If the message corresponding to the header being received
|
||||
contains a message body, it is the callers responsibility
|
||||
to cause the body to be read in before attempting to read
|
||||
the next message.
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b AsyncReadStream concept.
|
||||
|
||||
@param stream The stream to read the message from.
|
||||
The type must support the @b `AsyncReadStream` concept.
|
||||
|
||||
@param dynabuf A @b `DynamicBuffer` holding additional bytes
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object used to store the header. Any contents
|
||||
will be overwritten. The type must support copy assignment or
|
||||
move assignment. The object must remain valid at least until
|
||||
the completion handler is called; ownership is not transferred.
|
||||
@param parser The parser to use.
|
||||
|
||||
@param handler The handler to be called when the operation
|
||||
@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(
|
||||
@@ -150,20 +313,24 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
*/
|
||||
template<class AsyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class ReadHandler>
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
class ReadHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
header<isRequest, Fields>& msg,
|
||||
ReadHandler&& handler);
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
ReadHandler&& handler);
|
||||
|
||||
/** Read a HTTP/1 message from a stream.
|
||||
/** Read an HTTP/1 message from a stream.
|
||||
|
||||
This function is used to synchronously read a message from
|
||||
a stream. The call blocks until one of the following conditions
|
||||
@@ -176,7 +343,7 @@ async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
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 message
|
||||
being parsed. This additional data is stored in the stream
|
||||
being parsed. This additional data is stored in the dynamic
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
@@ -185,7 +352,7 @@ async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object used to store the message. Any
|
||||
@@ -194,10 +361,14 @@ async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& msg);
|
||||
|
||||
/** Read a HTTP/1 message from a stream.
|
||||
@@ -213,7 +384,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
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 message
|
||||
being parsed. This additional data is stored in the stream
|
||||
being parsed. This additional data is stored in the dynamic
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
@@ -222,7 +393,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object used to store the message. Any
|
||||
@@ -231,12 +402,16 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
error_code& ec);
|
||||
error_code& ec);
|
||||
|
||||
/** Read a HTTP/1 message asynchronously from a stream.
|
||||
|
||||
@@ -255,7 +430,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
stream performs no other operations until this operation completes.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the message being parsed. This additional data is stored
|
||||
in the stream buffer, which may be used in subsequent calls.
|
||||
in the dynamic buffer, which may be used in subsequent calls.
|
||||
|
||||
@param stream The stream to read the message from.
|
||||
The type must support the @b `AsyncReadStream` concept.
|
||||
@@ -263,7 +438,7 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
@param dynabuf 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
|
||||
stream buffer's input sequence will be given to the parser
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param msg An object used to store the header. Any contents
|
||||
@@ -282,22 +457,27 @@ read(SyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
*/
|
||||
template<class AsyncReadStream, class DynamicBuffer,
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class ReadHandler>
|
||||
class ReadHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_read(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
ReadHandler&& handler);
|
||||
ReadHandler&& handler);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/async_read.ipp>
|
||||
#include <beast/http/impl/read.ipp>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/detail/rfc7230.hpp>
|
||||
#include <beast/http/detail/basic_parsed_list.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
@@ -276,6 +277,46 @@ public:
|
||||
exists(T const& s);
|
||||
};
|
||||
|
||||
/** A list of tokens in a comma separated HTTP field value.
|
||||
|
||||
This container allows iteration of a list of items in a
|
||||
header field value. The input is a comma separated list of
|
||||
tokens.
|
||||
|
||||
If a parsing error is encountered while iterating the string,
|
||||
the behavior of the container will be as if a string containing
|
||||
only characters up to but excluding the first invalid character
|
||||
was used to construct the list.
|
||||
|
||||
@par BNF
|
||||
@code
|
||||
token-list = *( "," OWS ) token *( OWS "," [ OWS token ] )
|
||||
@endcode
|
||||
|
||||
To use this class, construct with the string to be parsed and
|
||||
then use `begin` and `end`, or range-for to iterate each item:
|
||||
|
||||
@par Example
|
||||
@code
|
||||
for(auto const& token : token_list{"apple, pear, banana"})
|
||||
std::cout << token << "\n";
|
||||
@endcode
|
||||
*/
|
||||
using opt_token_list =
|
||||
detail::basic_parsed_list<
|
||||
detail::opt_token_list_policy>;
|
||||
|
||||
/** Returns `true` if a parsed list is parsed without errors.
|
||||
|
||||
This function iterates a single pass through a parsed list
|
||||
and returns `true` if there were no parsing errors, else
|
||||
returns `false`.
|
||||
*/
|
||||
template<class Policy>
|
||||
bool
|
||||
validate_list(detail::basic_parsed_list<
|
||||
Policy> const& list);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
|
||||
@@ -34,29 +34,58 @@ private:
|
||||
|
||||
class reader
|
||||
{
|
||||
value_type& s_;
|
||||
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
|
||||
reader(message<isRequest,
|
||||
string_body, Fields>& m) noexcept
|
||||
: s_(m.body)
|
||||
string_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(error_code&) noexcept
|
||||
init()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
write(void const* data,
|
||||
std::size_t size, error_code&) noexcept
|
||||
init(std::uint64_t content_length)
|
||||
{
|
||||
auto const n = s_.size();
|
||||
s_.resize(n + size);
|
||||
std::memcpy(&s_[n], data, size);
|
||||
if(content_length >
|
||||
(std::numeric_limits<std::size_t>::max)())
|
||||
throw 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_);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#ifndef BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
|
||||
#define BEAST_WEBSOCKET_DETAIL_DECORATOR_HPP
|
||||
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/version.hpp>
|
||||
@@ -19,9 +18,9 @@ namespace beast {
|
||||
namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
using request_type = http::request<http::empty_body>;
|
||||
using request_type = http::request_header;
|
||||
|
||||
using response_type = http::response<http::string_body>;
|
||||
using response_type = http::response_header;
|
||||
|
||||
struct abstract_decorator
|
||||
{
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <beast/websocket/detail/mask.hpp>
|
||||
#include <beast/websocket/detail/pmd_extension.hpp>
|
||||
#include <beast/websocket/detail/utf8_checker.hpp>
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/zlib/deflate_stream.hpp>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#define BEAST_WEBSOCKET_IMPL_ACCEPT_IPP
|
||||
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
#include <beast/http/header_parser.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
@@ -35,13 +35,13 @@ class stream<NextLayer>::response_op
|
||||
{
|
||||
bool cont;
|
||||
stream<NextLayer>& ws;
|
||||
http::response<http::string_body> res;
|
||||
http::response_header res;
|
||||
error_code final_ec;
|
||||
int state = 0;
|
||||
|
||||
template<class Body, class Fields>
|
||||
template<class Fields>
|
||||
data(Handler&, stream<NextLayer>& ws_,
|
||||
http::request<Body, Fields> const& req,
|
||||
http::header<true, Fields> const& req,
|
||||
bool cont_)
|
||||
: cont(cont_)
|
||||
, ws(ws_)
|
||||
@@ -151,7 +151,7 @@ class stream<NextLayer>::accept_op
|
||||
{
|
||||
bool cont;
|
||||
stream<NextLayer>& ws;
|
||||
http::request<http::string_body> req;
|
||||
http::header_parser<true, http::fields> p;
|
||||
int state = 0;
|
||||
|
||||
template<class Buffers>
|
||||
@@ -190,8 +190,8 @@ public:
|
||||
(*this)(ec, 0);
|
||||
}
|
||||
|
||||
void operator()(error_code const& ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
void operator()(error_code ec,
|
||||
std::size_t bytes_used, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
@@ -228,36 +228,39 @@ template<class NextLayer>
|
||||
template<class Handler>
|
||||
void
|
||||
stream<NextLayer>::accept_op<Handler>::
|
||||
operator()(error_code const& ec,
|
||||
std::size_t bytes_transferred, bool again)
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_used, bool again)
|
||||
{
|
||||
beast::detail::ignore_unused(bytes_transferred);
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(! ec && d.state != 99)
|
||||
if(ec)
|
||||
goto upcall;
|
||||
switch(d.state)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
// read message
|
||||
d.state = 1;
|
||||
http::async_read(d.ws.next_layer(),
|
||||
d.ws.stream_.buffer(), d.req,
|
||||
std::move(*this));
|
||||
return;
|
||||
case 0:
|
||||
// read message
|
||||
d.state = 1;
|
||||
http::async_read_some(d.ws.next_layer(),
|
||||
d.ws.stream_.buffer(), d.p,
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got message
|
||||
case 1:
|
||||
{
|
||||
// respond to request
|
||||
auto& ws = d.ws;
|
||||
auto req = std::move(d.req);
|
||||
response_op<Handler>{
|
||||
d_.release_handler(), ws, req, true};
|
||||
return;
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
BOOST_ASSERT(d.p.got_header());
|
||||
d.ws.stream_.buffer().consume(bytes_used);
|
||||
// Arguments from our state must be
|
||||
// moved to the stack before releasing
|
||||
// the handler.
|
||||
auto& ws = d.ws;
|
||||
auto m = d.p.release();
|
||||
response_op<Handler>{
|
||||
d_.release_handler(),
|
||||
ws, std::move(m), true};
|
||||
return;
|
||||
}
|
||||
}
|
||||
upcall:
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
@@ -295,11 +298,11 @@ async_accept(ConstBufferSequence const& bs, AcceptHandler&& handler)
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Fields, class AcceptHandler>
|
||||
template<class Fields, class AcceptHandler>
|
||||
typename async_completion<
|
||||
AcceptHandler, void(error_code)>::result_type
|
||||
stream<NextLayer>::
|
||||
async_accept(http::request<Body, Fields> const& req,
|
||||
async_accept(http::header<true, Fields> const& req,
|
||||
AcceptHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncStream<next_layer_type>::value,
|
||||
@@ -372,18 +375,21 @@ accept(ConstBufferSequence const& buffers, error_code& ec)
|
||||
stream_.buffer().commit(buffer_copy(
|
||||
stream_.buffer().prepare(
|
||||
buffer_size(buffers)), buffers));
|
||||
http::request<http::string_body> m;
|
||||
http::read(next_layer(), stream_.buffer(), m, ec);
|
||||
http::header_parser<true, http::fields> p;
|
||||
auto const bytes_used = http::read_some(
|
||||
next_layer(), stream_.buffer(), p, ec);
|
||||
if(ec)
|
||||
return;
|
||||
accept(m, ec);
|
||||
BOOST_ASSERT(p.got_header());
|
||||
stream_.buffer().consume(bytes_used);
|
||||
accept(p.get(), ec);
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Fields>
|
||||
template<class Fields>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(http::request<Body, Fields> const& request)
|
||||
accept(http::header<true, Fields> const& request)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
"SyncStream requirements not met");
|
||||
@@ -394,10 +400,10 @@ accept(http::request<Body, Fields> const& request)
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Fields>
|
||||
template<class Fields>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
accept(http::request<Body, Fields> const& req,
|
||||
accept(http::header<true, Fields> const& req,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncStream<next_layer_type>::value,
|
||||
@@ -418,8 +424,6 @@ accept(http::request<Body, Fields> const& req,
|
||||
open(detail::role_type::server);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // websocket
|
||||
} // beast
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
#ifndef BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
|
||||
#define BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP
|
||||
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/streambuf_body.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
@@ -34,8 +34,8 @@ class stream<NextLayer>::handshake_op
|
||||
bool cont;
|
||||
stream<NextLayer>& ws;
|
||||
std::string key;
|
||||
http::request<http::empty_body> req;
|
||||
http::response<http::string_body> resp;
|
||||
http::request_header req;
|
||||
http::response<http::streambuf_body> res;
|
||||
int state = 0;
|
||||
|
||||
data(Handler& handler, stream<NextLayer>& ws_,
|
||||
@@ -129,14 +129,14 @@ operator()(error_code ec, bool again)
|
||||
// read http response
|
||||
d.state = 2;
|
||||
http::async_read(d.ws.next_layer(),
|
||||
d.ws.stream_.buffer(), d.resp,
|
||||
d.ws.stream_.buffer(), d.res,
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got response
|
||||
case 2:
|
||||
{
|
||||
d.ws.do_response(d.resp, d.key, ec);
|
||||
d.ws.do_response(d.res, d.key, ec);
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
@@ -196,7 +196,7 @@ handshake(boost::string_ref const& host,
|
||||
}
|
||||
if(ec)
|
||||
return;
|
||||
http::response<http::string_body> res;
|
||||
http::response<http::streambuf_body> res;
|
||||
http::read(next_layer(), stream_.buffer(), res, ec);
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
@@ -83,17 +83,18 @@ reset()
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
http::request<http::empty_body>
|
||||
http::request_header
|
||||
stream<NextLayer>::
|
||||
build_request(boost::string_ref const& host,
|
||||
boost::string_ref const& resource, std::string& key)
|
||||
{
|
||||
http::request<http::empty_body> req;
|
||||
http::request_header req;
|
||||
req.url = { resource.data(), resource.size() };
|
||||
req.version = 11;
|
||||
req.method = "GET";
|
||||
req.fields.insert("Host", host);
|
||||
req.fields.insert("Upgrade", "websocket");
|
||||
req.fields.insert("Connection", "upgrade");
|
||||
key = detail::make_sec_ws_key(maskgen_);
|
||||
req.fields.insert("Sec-WebSocket-Key", key);
|
||||
req.fields.insert("Sec-WebSocket-Version", "13");
|
||||
@@ -113,15 +114,13 @@ build_request(boost::string_ref const& host,
|
||||
req.fields, config);
|
||||
}
|
||||
d_(req);
|
||||
http::prepare(req, http::connection::upgrade);
|
||||
return req;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Fields>
|
||||
http::response<http::string_body>
|
||||
http::response_header
|
||||
stream<NextLayer>::
|
||||
build_response(http::request<Body, Fields> const& req)
|
||||
build_response(http::request_header const& req)
|
||||
{
|
||||
auto err =
|
||||
[&](std::string const& text)
|
||||
@@ -170,7 +169,7 @@ build_response(http::request<Body, Fields> const& req)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
http::response<http::string_body> res;
|
||||
http::response_header res;
|
||||
{
|
||||
detail::pmd_offer offer;
|
||||
detail::pmd_offer unused;
|
||||
@@ -182,23 +181,22 @@ build_response(http::request<Body, Fields> const& req)
|
||||
res.reason = http::reason_string(res.status);
|
||||
res.version = req.version;
|
||||
res.fields.insert("Upgrade", "websocket");
|
||||
res.fields.insert("Connection", "upgrade");
|
||||
{
|
||||
auto const key =
|
||||
req.fields["Sec-WebSocket-Key"];
|
||||
res.fields.insert("Sec-WebSocket-Accept",
|
||||
detail::make_sec_ws_accept(key));
|
||||
}
|
||||
res.fields.replace("Server", "Beast.WSProto");
|
||||
res.fields.replace("Server", "Beast.WebSocket");
|
||||
d_(res);
|
||||
http::prepare(res, http::connection::upgrade);
|
||||
return res;
|
||||
}
|
||||
|
||||
template<class NextLayer>
|
||||
template<class Body, class Fields>
|
||||
void
|
||||
stream<NextLayer>::
|
||||
do_response(http::response<Body, Fields> const& res,
|
||||
do_response(http::response_header const& res,
|
||||
boost::string_ref const& key, error_code& ec)
|
||||
{
|
||||
// VFALCO Review these error codes
|
||||
|
||||
@@ -597,9 +597,9 @@ public:
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
// VFALCO TODO This should also take a DynamicBuffer with any leftover bytes.
|
||||
template<class Body, class Fields>
|
||||
template<class Fields>
|
||||
void
|
||||
accept(http::request<Body, Fields> const& request);
|
||||
accept(http::header<true, Fields> const& request);
|
||||
|
||||
/** Respond to a WebSocket HTTP Upgrade request
|
||||
|
||||
@@ -629,10 +629,9 @@ public:
|
||||
|
||||
@param ec Set to indicate what error occurred, if any.
|
||||
*/
|
||||
template<class Body, class Fields>
|
||||
template<class Fields>
|
||||
void
|
||||
accept(http::request<Body, Fields> const& request,
|
||||
error_code& ec);
|
||||
accept(http::header<true, Fields> const& request, error_code& ec);
|
||||
|
||||
/** Start responding to a WebSocket HTTP Upgrade request.
|
||||
|
||||
@@ -675,15 +674,15 @@ public:
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
*/
|
||||
template<class Body, class Fields, class AcceptHandler>
|
||||
template<class Fields, class AcceptHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
AcceptHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_accept(http::request<Body, Fields> const& request,
|
||||
AcceptHandler&& handler);
|
||||
async_accept(http::header<true,
|
||||
Fields> const& request, AcceptHandler&& handler);
|
||||
|
||||
/** Send a HTTP WebSocket Upgrade request and receive the response.
|
||||
|
||||
@@ -1674,18 +1673,16 @@ private:
|
||||
void
|
||||
reset();
|
||||
|
||||
http::request<http::empty_body>
|
||||
http::request_header
|
||||
build_request(boost::string_ref const& host,
|
||||
boost::string_ref const& resource,
|
||||
std::string& key);
|
||||
|
||||
template<class Body, class Fields>
|
||||
http::response<http::string_body>
|
||||
build_response(http::request<Body, Fields> const& req);
|
||||
http::response_header
|
||||
build_response(http::request_header const& req);
|
||||
|
||||
template<class Body, class Fields>
|
||||
void
|
||||
do_response(http::response<Body, Fields> const& resp,
|
||||
do_response(http::response_header const& resp,
|
||||
boost::string_ref const& key, error_code& ec);
|
||||
};
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ public:
|
||||
of bytes needed to store the result of compressing a block of
|
||||
data based on the current compression level and strategy.
|
||||
|
||||
@param bytes The size of the uncompressed data.
|
||||
@param sourceLen The size of the uncompressed data.
|
||||
|
||||
@return The maximum number of resulting compressed bytes.
|
||||
*/
|
||||
|
||||
@@ -181,7 +181,7 @@ public:
|
||||
`Flush::trees` is used, and when `write` avoids the allocation of memory for a
|
||||
sliding window when `Flush::finsih` is used.
|
||||
|
||||
If a preset dictionary is needed after this call (see @ref dictionary below),
|
||||
If a preset dictionary is needed after this call,
|
||||
`write` sets `zs.adler` to the Adler-32 checksum of the dictionary chosen by
|
||||
the compressor and returns `error::need_dictionary`; otherwise it sets
|
||||
`zs.adler` to the Adler-32 checksum of all output produced so far (that is,
|
||||
|
||||
Reference in New Issue
Block a user