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:
Vinnie Falco committed 2017-07-20 08:12:15 -07:00
1 parent 34830a1780
commit e8be3fd7d3
74 files changed
+7106 -5972

No files matched your search

+716
View File
@@ -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
+106
View File
@@ -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
+25
View File
@@ -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
-302
View File
@@ -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
-105
View File
@@ -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
View File
@@ -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
+20
View File
@@ -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