mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Parser concept, fixes:
A new concept Parser is introduced with routines to read from a stream into the parser. This solves a problem with the old read interface where messages must be default constructible and move assignable. Parser fixes: * Fix detect invalid reason-phrase octets * Fix write_eof to set the 'complete' state on success * Fix consider parse complete if eof received on empty body WebSocket: * Increase coverage
This commit is contained in:
@@ -855,8 +855,11 @@ std::size_t
|
||||
read_size_helper(basic_streambuf<
|
||||
Allocator> const& streambuf, std::size_t max_size)
|
||||
{
|
||||
return std::min<std::size_t>(max_size,
|
||||
std::max<std::size_t>(512, streambuf.prepare_size()));
|
||||
auto const avail = streambuf.prepare_size();
|
||||
if(avail == 0)
|
||||
return std::min(max_size,
|
||||
std::max<std::size_t>(512, streambuf.alloc_size_));
|
||||
return std::min(max_size, avail);
|
||||
}
|
||||
|
||||
template<class Alloc, class T>
|
||||
|
||||
@@ -36,11 +36,26 @@ enum values
|
||||
};
|
||||
} // parse_flag
|
||||
|
||||
/** Base class for parsing HTTP/1 requests and responses.
|
||||
/** A parser for decoding HTTP/1 wire format messages.
|
||||
|
||||
During parsing, callbacks will be made to the derived class
|
||||
if those members are present (detected through SFINAE). The
|
||||
signatures which can be present in the derived class are:<br>
|
||||
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.
|
||||
|
||||
Callbacks are detected through SFINAE. The derived class may
|
||||
implement as few or as many of the members as needed.
|
||||
These are the signatures of the callbacks:<br>
|
||||
|
||||
@li `void on_start(error_code&)`
|
||||
|
||||
Called when the first valid octet of a new message is received
|
||||
|
||||
@li `void on_method(boost::string_ref const&, error_code&)`
|
||||
|
||||
@@ -106,6 +121,9 @@ enum values
|
||||
|
||||
If a callback sets an error, parsing stops at the current octet
|
||||
and the error is returned to the caller.
|
||||
|
||||
@tparam isRequest A `bool` indicating whether the parser will be
|
||||
presented with request or response message.
|
||||
*/
|
||||
template<bool isRequest, class Derived>
|
||||
class basic_parser_v1
|
||||
@@ -188,7 +206,8 @@ private:
|
||||
s_chunk_data_done,
|
||||
|
||||
s_complete,
|
||||
s_restart
|
||||
s_restart,
|
||||
s_closed_complete
|
||||
};
|
||||
|
||||
enum field_state : std::uint8_t
|
||||
@@ -341,7 +360,7 @@ public:
|
||||
bool
|
||||
complete() const
|
||||
{
|
||||
return s_ == s_restart;
|
||||
return s_ == s_restart || s_ == s_closed_complete;
|
||||
}
|
||||
|
||||
/** Write a sequence of buffers to the parser.
|
||||
@@ -411,6 +430,24 @@ private:
|
||||
bool
|
||||
needs_eof(std::false_type) const;
|
||||
|
||||
template<class C>
|
||||
class has_on_start_t
|
||||
{
|
||||
template<class T, class R =
|
||||
decltype(std::declval<T>().on_start(
|
||||
std::declval<error_code&>()),
|
||||
std::true_type{})>
|
||||
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 has_on_start =
|
||||
std::integral_constant<bool, has_on_start_t<C>::value>;
|
||||
|
||||
template<class C>
|
||||
class has_on_method_t
|
||||
{
|
||||
@@ -596,6 +633,20 @@ private:
|
||||
using has_on_complete =
|
||||
std::integral_constant<bool, has_on_complete_t<C>::value>;
|
||||
|
||||
void call_on_start(error_code& ec, std::true_type)
|
||||
{
|
||||
impl().on_start(ec);
|
||||
}
|
||||
|
||||
void call_on_start(error_code& ec, std::false_type)
|
||||
{
|
||||
}
|
||||
|
||||
void call_on_start(error_code& ec)
|
||||
{
|
||||
call_on_start(ec, has_on_start<Derived>{});
|
||||
}
|
||||
|
||||
void call_on_method(error_code& ec,
|
||||
boost::string_ref const& s, std::true_type)
|
||||
{
|
||||
|
||||
@@ -132,7 +132,7 @@ to_value_char(char c)
|
||||
}
|
||||
|
||||
inline
|
||||
std::uint8_t
|
||||
std::int8_t
|
||||
unhex(char c)
|
||||
{
|
||||
static std::array<std::int8_t, 256> constexpr tab = {{
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define BEAST_HTTP_IMPL_BASIC_PARSER_V1_IPP
|
||||
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <cassert>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
@@ -88,6 +89,11 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
s_ = s_closed;
|
||||
return used();
|
||||
};
|
||||
auto errc = [&]
|
||||
{
|
||||
s_ = s_closed;
|
||||
return used();
|
||||
};
|
||||
auto piece = [&]
|
||||
{
|
||||
return boost::string_ref{
|
||||
@@ -113,6 +119,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
switch(s_)
|
||||
{
|
||||
case s_closed:
|
||||
case s_closed_complete:
|
||||
return err(parse_error::connection_closed);
|
||||
break;
|
||||
|
||||
@@ -126,6 +133,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
case s_req_method_start:
|
||||
if(! is_token(ch))
|
||||
return err(parse_error::bad_method);
|
||||
call_on_start(ec);
|
||||
if(ec)
|
||||
return errc();
|
||||
cb_ = &self::call_on_method;
|
||||
s_ = s_req_method;
|
||||
break;
|
||||
@@ -134,7 +144,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
if(! is_token(ch))
|
||||
{
|
||||
if(cb(nullptr))
|
||||
return used();
|
||||
return errc();
|
||||
s_ = s_req_space_before_url;
|
||||
goto redo;
|
||||
}
|
||||
@@ -147,21 +157,23 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
break;
|
||||
|
||||
case s_req_url_start:
|
||||
{
|
||||
if(ch == ' ')
|
||||
return err(parse_error::bad_uri);
|
||||
// VFALCO TODO Better checking for valid URL characters
|
||||
if(! is_text(ch))
|
||||
return err(parse_error::bad_uri);
|
||||
if(cb(&self::call_on_uri))
|
||||
return used();
|
||||
assert(! cb_);
|
||||
cb(&self::call_on_uri);
|
||||
s_ = s_req_url;
|
||||
break;
|
||||
}
|
||||
|
||||
case s_req_url:
|
||||
if(ch == ' ')
|
||||
{
|
||||
if(cb(nullptr))
|
||||
return used();
|
||||
return errc();
|
||||
s_ = s_req_http_start;
|
||||
break;
|
||||
}
|
||||
@@ -245,7 +257,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
return err(parse_error::bad_crlf);
|
||||
call_on_request(ec);
|
||||
if(ec)
|
||||
return used();
|
||||
return errc();
|
||||
s_ = s_header_field_start;
|
||||
break;
|
||||
|
||||
@@ -257,7 +269,14 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
content_length_ = no_content_length;
|
||||
switch(ch)
|
||||
{
|
||||
case 'H': s_ = s_res_H; break;
|
||||
case 'H':
|
||||
call_on_start(ec);
|
||||
if(ec)
|
||||
return errc();
|
||||
s_ = s_res_H;
|
||||
break;
|
||||
// VFALCO NOTE this allows whitespace at the beginning,
|
||||
// need to check rfc7230
|
||||
case '\r':
|
||||
case '\n':
|
||||
break;
|
||||
@@ -365,13 +384,16 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
s_ = s_res_line_almost_done;
|
||||
break;
|
||||
}
|
||||
// VFALCO Is this up to spec?
|
||||
if(ch == '\n')
|
||||
{
|
||||
s_ = s_header_field_start;
|
||||
break;
|
||||
}
|
||||
if(! is_text(ch))
|
||||
return err(parse_error::bad_status);
|
||||
if(cb(&self::call_on_reason))
|
||||
return used();
|
||||
return errc();
|
||||
pos_ = 0;
|
||||
s_ = s_res_status;
|
||||
break;
|
||||
@@ -380,17 +402,19 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
if(ch == '\r')
|
||||
{
|
||||
if(cb(nullptr))
|
||||
return used();
|
||||
return errc();
|
||||
s_ = s_res_line_almost_done;
|
||||
break;
|
||||
}
|
||||
if(ch == '\n')
|
||||
{
|
||||
if(cb(nullptr))
|
||||
return used();
|
||||
return errc();
|
||||
s_ = s_header_field_start;
|
||||
break;
|
||||
}
|
||||
if(! is_text(ch))
|
||||
return err(parse_error::bad_status);
|
||||
break;
|
||||
|
||||
case s_res_line_almost_done:
|
||||
@@ -402,7 +426,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
case s_res_line_done:
|
||||
call_on_response(ec);
|
||||
if(ec)
|
||||
return used();
|
||||
return errc();
|
||||
s_ = s_header_field_start;
|
||||
goto redo;
|
||||
|
||||
@@ -431,8 +455,8 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
fs_ = h_general;
|
||||
break;
|
||||
}
|
||||
if(cb(&self::call_on_field))
|
||||
return used();
|
||||
assert(! cb_);
|
||||
cb(&self::call_on_field);
|
||||
s_ = s_header_field;
|
||||
break;
|
||||
}
|
||||
@@ -529,7 +553,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
if(ch == ':')
|
||||
{
|
||||
if(cb(nullptr))
|
||||
return used();
|
||||
return errc();
|
||||
s_ = s_header_value_start;
|
||||
break;
|
||||
}
|
||||
@@ -579,7 +603,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
}
|
||||
call_on_value(ec, boost::string_ref{"", 0});
|
||||
if(ec)
|
||||
return used();
|
||||
return errc();
|
||||
s_ = s_header_field_start;
|
||||
goto redo;
|
||||
|
||||
@@ -629,7 +653,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
}
|
||||
pos_ = 0;
|
||||
if(cb(&self::call_on_value))
|
||||
return used();
|
||||
return errc();
|
||||
s_ = s_header_value_text;
|
||||
break;
|
||||
}
|
||||
@@ -641,7 +665,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
if(ch == '\r')
|
||||
{
|
||||
if(cb(nullptr))
|
||||
return used();
|
||||
return errc();
|
||||
s_ = s_header_value_discard_lWs;
|
||||
break;
|
||||
}
|
||||
@@ -775,9 +799,9 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
return err(parse_error::bad_value);
|
||||
call_on_value(ec, boost::string_ref(" ", 1));
|
||||
if(ec)
|
||||
return used();
|
||||
return errc();
|
||||
if(cb(&self::call_on_value))
|
||||
return used();
|
||||
return errc();
|
||||
s_ = s_header_value_text;
|
||||
break;
|
||||
|
||||
@@ -811,7 +835,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
return err(parse_error::bad_crlf);
|
||||
if(flags_ & parse_flag::trailing)
|
||||
{
|
||||
//if(cb(&self::call_on_chunk_complete)) return used();
|
||||
//if(cb(&self::call_on_chunk_complete)) return errc();
|
||||
s_ = s_complete;
|
||||
goto redo;
|
||||
}
|
||||
@@ -821,7 +845,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
(parse_flag::upgrade | parse_flag::connection_upgrade)) /*|| method == "connect"*/;
|
||||
auto const maybe_skip = call_on_headers(ec);
|
||||
if(ec)
|
||||
return used();
|
||||
return errc();
|
||||
switch(maybe_skip)
|
||||
{
|
||||
case 0: break;
|
||||
@@ -839,7 +863,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
assert(! cb_);
|
||||
call_on_headers(ec);
|
||||
if(ec)
|
||||
return used();
|
||||
return errc();
|
||||
bool const hasBody =
|
||||
(flags_ & parse_flag::chunked) || (content_length_ > 0 &&
|
||||
content_length_ != no_content_length);
|
||||
@@ -878,8 +902,8 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
}
|
||||
|
||||
case s_body_identity0:
|
||||
if(cb(&self::call_on_body))
|
||||
return used();
|
||||
assert(! cb_);
|
||||
cb(&self::call_on_body);
|
||||
s_ = s_body_identity;
|
||||
goto redo; // VFALCO fall through?
|
||||
|
||||
@@ -903,8 +927,8 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
}
|
||||
|
||||
case s_body_identity_eof0:
|
||||
if(cb(&self::call_on_body))
|
||||
return used();
|
||||
assert(! cb_);
|
||||
cb(&self::call_on_body);
|
||||
s_ = s_body_identity_eof;
|
||||
goto redo; // VFALCO fall through?
|
||||
|
||||
@@ -963,13 +987,13 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
s_ = s_header_field_start;
|
||||
break;
|
||||
}
|
||||
//call_chunk_header(ec); if(ec) return used();
|
||||
//call_chunk_header(ec); if(ec) return errc();
|
||||
s_ = s_chunk_data_start;
|
||||
break;
|
||||
|
||||
case s_chunk_data_start:
|
||||
if(cb(&self::call_on_body))
|
||||
return used();
|
||||
assert(! cb_);
|
||||
cb(&self::call_on_body);
|
||||
s_ = s_chunk_data;
|
||||
goto redo; // VFALCO fall through?
|
||||
|
||||
@@ -991,7 +1015,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
if(ch != '\r')
|
||||
return err(parse_error::bad_crlf);
|
||||
if(cb(nullptr))
|
||||
return used();
|
||||
return errc();
|
||||
s_ = s_chunk_data_done;
|
||||
break;
|
||||
|
||||
@@ -1005,10 +1029,10 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
case s_complete:
|
||||
++p;
|
||||
if(cb(nullptr))
|
||||
return used();
|
||||
return errc();
|
||||
call_on_complete(ec);
|
||||
if(ec)
|
||||
return used();
|
||||
return errc();
|
||||
s_ = s_restart;
|
||||
return used();
|
||||
|
||||
@@ -1024,7 +1048,7 @@ write(boost::asio::const_buffer const& buffer, error_code& ec)
|
||||
{
|
||||
(this->*cb_)(ec, piece());
|
||||
if(ec)
|
||||
return used();
|
||||
return errc();
|
||||
}
|
||||
return used();
|
||||
}
|
||||
@@ -1036,17 +1060,31 @@ write_eof(error_code& ec)
|
||||
{
|
||||
switch(s_)
|
||||
{
|
||||
case s_restart:
|
||||
s_ = s_closed_complete;
|
||||
break;
|
||||
|
||||
case s_closed:
|
||||
case s_closed_complete:
|
||||
break;
|
||||
|
||||
case s_body_identity_eof0:
|
||||
case s_body_identity_eof:
|
||||
cb_ = nullptr;
|
||||
call_on_complete(ec);
|
||||
if(ec)
|
||||
return;
|
||||
return;
|
||||
{
|
||||
s_ = s_closed;
|
||||
break;
|
||||
}
|
||||
s_ = s_closed_complete;
|
||||
break;
|
||||
|
||||
default:
|
||||
s_ = s_closed;
|
||||
ec = parse_error::short_read;
|
||||
break;
|
||||
}
|
||||
ec = parse_error::short_read;
|
||||
s_ = s_closed;
|
||||
}
|
||||
|
||||
template<bool isRequest, class Derived>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#define BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
#include <beast/http/type_check.hpp>
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/handler_alloc.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
@@ -19,6 +20,185 @@ namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Stream,
|
||||
class Streambuf, class Parser, class Handler>
|
||||
class parse_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
Stream& s;
|
||||
Streambuf& sb;
|
||||
Parser& p;
|
||||
Handler h;
|
||||
bool started = false;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, Stream& s_,
|
||||
Streambuf& sb_, Parser& p_)
|
||||
: s(s_)
|
||||
, sb(sb_)
|
||||
, p(p_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> 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::allocate_shared<data>(alloc_type{h},
|
||||
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 boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, parse_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
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 boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream,
|
||||
class Streambuf, class Parser, class Handler>
|
||||
void
|
||||
parse_op<Stream, Streambuf, 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:
|
||||
{
|
||||
auto const used =
|
||||
d.p.write(d.sb.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.started = true;
|
||||
d.sb.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;
|
||||
}
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
// read
|
||||
d.state = 2;
|
||||
d.s.async_read_some(d.sb.prepare(
|
||||
read_size_helper(d.sb, 65536)),
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got data
|
||||
case 2:
|
||||
{
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
if(! d.started)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
// Caller will see eof on next read.
|
||||
ec = {};
|
||||
d.p.write_eof(ec);
|
||||
assert(ec || d.p.complete());
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
d.sb.commit(bytes_transferred);
|
||||
auto const used = d.p.write(d.sb.data(), ec);
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(used > 0)
|
||||
d.started = true;
|
||||
d.sb.consume(used);
|
||||
if(d.p.complete())
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class Streambuf,
|
||||
bool isRequest, class Body, class Headers,
|
||||
class Handler>
|
||||
@@ -69,12 +249,11 @@ public:
|
||||
std::forward<DeducedHandler>(h), s,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
(*this)(error_code{}, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
operator()(error_code ec, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
@@ -112,98 +291,25 @@ template<class Stream, class Streambuf,
|
||||
class Handler>
|
||||
void
|
||||
read_op<Stream, Streambuf, isRequest, Body, Headers, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_transferred, bool again)
|
||||
operator()(error_code ec, bool again)
|
||||
{
|
||||
auto& d = *d_;
|
||||
d.cont = d.cont || again;
|
||||
while(d.state != 99)
|
||||
while(! ec && d.state != 99)
|
||||
{
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
auto const used =
|
||||
d.p.write(d.sb.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.started = true;
|
||||
d.sb.consume(used);
|
||||
if(d.p.complete())
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.m = d.p.release();
|
||||
d.s.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
return;
|
||||
}
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
// read
|
||||
d.state = 2;
|
||||
d.s.async_read_some(d.sb.prepare(
|
||||
read_size_helper(d.sb, 65536)),
|
||||
std::move(*this));
|
||||
async_parse(d.s, d.sb, d.p, std::move(*this));
|
||||
return;
|
||||
|
||||
// got data
|
||||
case 2:
|
||||
{
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
if(! d.started)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
// Caller will see eof on next read.
|
||||
ec = {};
|
||||
d.p.write_eof(ec);
|
||||
if(! ec)
|
||||
{
|
||||
assert(d.p.complete());
|
||||
d.m = d.p.release();
|
||||
}
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
d.sb.commit(bytes_transferred);
|
||||
d.sb.consume(d.p.write(d.sb.data(), ec));
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(d.p.complete())
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.m = d.p.release();
|
||||
break;
|
||||
}
|
||||
d.state = 1;
|
||||
case 1:
|
||||
// call handler
|
||||
d.state = 99;
|
||||
d.m = d.p.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
}
|
||||
@@ -212,12 +318,91 @@ operator()(error_code ec, std::size_t bytes_transferred, bool again)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class SyncReadStream, class Streambuf, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream,
|
||||
Streambuf& streambuf, Parser& parser)
|
||||
{
|
||||
error_code ec;
|
||||
parse(stream, streambuf, parser, ec);
|
||||
if(ec)
|
||||
throw boost::system::system_error{ec};
|
||||
}
|
||||
|
||||
template<class SyncReadStream, class Streambuf, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream, Streambuf& streambuf,
|
||||
Parser& parser, error_code& ec)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_Streambuf<Streambuf>::value,
|
||||
"Streambuf requirements not met");
|
||||
static_assert(is_Parser<Parser>::value,
|
||||
"Parser requirements not met");
|
||||
bool started = false;
|
||||
for(;;)
|
||||
{
|
||||
auto used =
|
||||
parser.write(streambuf.data(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
streambuf.consume(used);
|
||||
if(used > 0)
|
||||
started = true;
|
||||
if(parser.complete())
|
||||
break;
|
||||
streambuf.commit(stream.read_some(
|
||||
streambuf.prepare(read_size_helper(
|
||||
streambuf, 65536)), ec));
|
||||
if(ec && ec != boost::asio::error::eof)
|
||||
return;
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
if(! started)
|
||||
return;
|
||||
// Caller will see eof on next read.
|
||||
ec = {};
|
||||
parser.write_eof(ec);
|
||||
if(ec)
|
||||
return;
|
||||
assert(parser.complete());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class AsyncReadStream,
|
||||
class Streambuf, class Parser, class ReadHandler>
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
async_parse(AsyncReadStream& stream,
|
||||
Streambuf& streambuf, Parser& parser, ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_Streambuf<Streambuf>::value,
|
||||
"Streambuf 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, Streambuf,
|
||||
Parser, decltype(completion.handler)>{
|
||||
completion.handler, stream, streambuf, parser};
|
||||
return completion.result.get();
|
||||
}
|
||||
|
||||
template<class SyncReadStream, class Streambuf,
|
||||
bool isRequest, class Body, class Headers>
|
||||
void
|
||||
read(SyncReadStream& stream, Streambuf& streambuf,
|
||||
message_v1<isRequest, Body, Headers>& msg)
|
||||
{
|
||||
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_Streambuf<Streambuf>::value,
|
||||
"Streambuf requirements not met");
|
||||
error_code ec;
|
||||
read(stream, streambuf, msg, ec);
|
||||
if(ec)
|
||||
@@ -236,40 +421,11 @@ read(SyncReadStream& stream, Streambuf& streambuf,
|
||||
static_assert(is_Streambuf<Streambuf>::value,
|
||||
"Streambuf requirements not met");
|
||||
parser_v1<isRequest, Body, Headers> p;
|
||||
bool started = false;
|
||||
for(;;)
|
||||
{
|
||||
auto used =
|
||||
p.write(streambuf.data(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
streambuf.consume(used);
|
||||
if(used > 0)
|
||||
started = true;
|
||||
if(p.complete())
|
||||
{
|
||||
m = p.release();
|
||||
break;
|
||||
}
|
||||
streambuf.commit(stream.read_some(
|
||||
streambuf.prepare(read_size_helper(
|
||||
streambuf, 65536)), ec));
|
||||
if(ec && ec != boost::asio::error::eof)
|
||||
return;
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
if(! started)
|
||||
return;
|
||||
// Caller will see eof on next read.
|
||||
ec = {};
|
||||
p.write_eof(ec);
|
||||
if(ec)
|
||||
return;
|
||||
assert(p.complete());
|
||||
m = p.release();
|
||||
break;
|
||||
}
|
||||
}
|
||||
parse(stream, streambuf, p, ec);
|
||||
if(ec)
|
||||
return;
|
||||
assert(p.complete());
|
||||
m = p.release();
|
||||
}
|
||||
|
||||
template<class AsyncReadStream, class Streambuf,
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include <beast/http/basic_parser_v1.hpp>
|
||||
#include <beast/http/message_v1.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
@@ -39,6 +38,8 @@ struct parser_response
|
||||
|
||||
This class uses the basic HTTP/1 wire format parser to convert
|
||||
a series of octets into a `message_v1`.
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Headers>
|
||||
class parser_v1
|
||||
@@ -47,9 +48,12 @@ class parser_v1
|
||||
, private std::conditional<isRequest,
|
||||
detail::parser_request, detail::parser_response>::type
|
||||
{
|
||||
public:
|
||||
/// The type of message this parser produces.
|
||||
using message_type =
|
||||
message_v1<isRequest, Body, Headers>;
|
||||
|
||||
private:
|
||||
std::string field_;
|
||||
std::string value_;
|
||||
message_type m_;
|
||||
@@ -57,15 +61,55 @@ class parser_v1
|
||||
|
||||
public:
|
||||
parser_v1(parser_v1&&) = default;
|
||||
parser_v1(parser_v1 const&) = delete;
|
||||
parser_v1& operator=(parser_v1&&) = delete;
|
||||
parser_v1& operator=(parser_v1 const&) = delete;
|
||||
|
||||
parser_v1()
|
||||
: r_(m_)
|
||||
/** Construct the parser.
|
||||
|
||||
@param args A list of arguments forwarded to the message constructor.
|
||||
*/
|
||||
template<class... Args>
|
||||
explicit
|
||||
parser_v1(Args&&... args)
|
||||
: m_(std::forward<Args>(args)...)
|
||||
, r_(m_)
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Only valid if `complete()` would return `true`.
|
||||
*/
|
||||
message_type const&
|
||||
get() const
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Only valid if `complete()` would return `true`.
|
||||
*/
|
||||
message_type&
|
||||
get()
|
||||
{
|
||||
return m_;
|
||||
}
|
||||
|
||||
/** Returns the parsed message.
|
||||
|
||||
Ownership is transferred to the caller.
|
||||
Only valid if `complete()` would return `true`.
|
||||
|
||||
Requires:
|
||||
`message<isRequest, Body, Headers>` is MoveConstructible
|
||||
*/
|
||||
message_type
|
||||
release()
|
||||
{
|
||||
static_assert(std::is_move_constructible<decltype(m_)>::value,
|
||||
"MoveConstructible requirements not met");
|
||||
return std::move(m_);
|
||||
}
|
||||
|
||||
@@ -84,6 +128,10 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void on_start(error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void on_method(boost::string_ref const& s, error_code&)
|
||||
{
|
||||
this->method_.append(s.data(), s.size());
|
||||
|
||||
@@ -17,6 +17,127 @@
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Parse a 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 are met:
|
||||
|
||||
@li A complete message is read in.
|
||||
|
||||
@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
|
||||
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 `SyncReadStream` concept.
|
||||
|
||||
@param streambuf A `Streambuf` 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 Parser
|
||||
which will receive the data.
|
||||
|
||||
@throws boost::system::system_error on failure.
|
||||
*/
|
||||
template<class SyncReadStream, class Streambuf, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream,
|
||||
Streambuf& streambuf, Parser& parser);
|
||||
|
||||
/** Parse a 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 are met:
|
||||
|
||||
@li A complete message is read in.
|
||||
|
||||
@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
|
||||
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 `SyncReadStream` concept.
|
||||
|
||||
@param streambuf A `Streambuf` 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 `Parser`
|
||||
which will receive the data.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
template<class SyncReadStream, class Streambuf, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream,
|
||||
Streambuf& streambuf, Parser& parser, error_code& ec);
|
||||
|
||||
/** Start an asynchronous operation to parse a HTTP/1 message 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 A complete message is read in.
|
||||
|
||||
@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.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b `AsyncReadStream` concept.
|
||||
|
||||
@param streambuf A `Streambuf` 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 `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 Streambuf, class Parser, class ReadHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_parse(AsyncReadStream& stream, Streambuf& streambuf,
|
||||
Parser& parser, ReadHandler&& handler);
|
||||
|
||||
/** Read a HTTP/1 message from a stream.
|
||||
|
||||
This function is used to synchronously read a message from
|
||||
@@ -25,18 +146,22 @@ namespace http {
|
||||
|
||||
@li A complete message is read in.
|
||||
|
||||
@li An error occurs on the stream.
|
||||
@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.
|
||||
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
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b `SyncReadStream` concept.
|
||||
|
||||
@param streambuf An object meeting the @b `Streambuf` type requirements
|
||||
used to hold unread bytes. The implementation may read past the end of
|
||||
the message. The extra bytes are stored here, to be presented in a
|
||||
subsequent call to @ref read.
|
||||
@param streambuf A `Streambuf` 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 msg An object used to store the message. Any
|
||||
contents will be overwritten.
|
||||
@@ -57,21 +182,25 @@ read(SyncReadStream& stream, Streambuf& streambuf,
|
||||
|
||||
@li A complete message is read in.
|
||||
|
||||
@li An error occurs on the stream.
|
||||
@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.
|
||||
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
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b `SyncReadStream` concept.
|
||||
|
||||
@param streambuf An object meeting the @b `Streambuf` type requirements
|
||||
used to hold unread bytes. The implementation may read past the end of
|
||||
the message. The extra bytes are stored here, to be presented in a
|
||||
subsequent call to @ref read.
|
||||
@param streambuf A `Streambuf` 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 msg An object used to store the message. Any contents
|
||||
will be overwritten.
|
||||
@param msg An object used to store the message. Any
|
||||
contents will be overwritten.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
@@ -90,7 +219,7 @@ read(SyncReadStream& stream, Streambuf& streambuf,
|
||||
|
||||
@li A complete message is read in.
|
||||
|
||||
@li An error occurs on the stream.
|
||||
@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
|
||||
@@ -100,10 +229,11 @@ read(SyncReadStream& stream, Streambuf& streambuf,
|
||||
@param stream The stream to read the message from.
|
||||
The type must support the @b `AsyncReadStream` concept.
|
||||
|
||||
@param streambuf A Streambuf used to hold unread bytes. The
|
||||
implementation may read past the end of the message. The extra
|
||||
bytes are stored here, to be presented in a subsequent call to
|
||||
@ref async_read.
|
||||
@param streambuf A `Streambuf` 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 msg An object used to store the message. Any contents
|
||||
will be overwritten.
|
||||
|
||||
@@ -40,11 +40,9 @@ class is_Parser
|
||||
static std::false_type check2(...);
|
||||
using type2 = decltype(check2<T>(0));
|
||||
|
||||
template<class U, class R =
|
||||
std::is_convertible<decltype(
|
||||
std::declval<U>().write_eof(
|
||||
std::declval<error_code&>())),
|
||||
std::size_t>>
|
||||
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(...);
|
||||
|
||||
Reference in New Issue
Block a user