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:
1 parent
34830a1780
commit
e8be3fd7d3
74 files changed
+7106
-5972
No files matched your search
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user