mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 21:34:46 +02:00
Refine Parser concept:
* Parser is not HTTP-specific * parse algorithms are in their own header * Update documentation
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
* Make value optional in param-list
|
||||
* Frame processing routines are member functions
|
||||
* Fix on_headers called twice from basic_parser_v1
|
||||
* Refine Parser concept
|
||||
|
||||
API Changes:
|
||||
|
||||
|
@@ -103,7 +103,7 @@ maximum inlining and optimization.
|
||||
Boost.Asio is the inspiration behind which all of the interfaces and
|
||||
implementation strategies are built. Some parts of the documentation are
|
||||
written to closely resemble the wording and presentation of Boost.Asio
|
||||
documentation. Credit goes to Christopher Kohloff for the wonderful
|
||||
documentation. Credit goes to Christopher Kohlhoff for the wonderful
|
||||
Asio library and the ideas upon which Beast is built.
|
||||
|
||||
Beast would not be possible without the considerable time and patience
|
||||
|
@@ -7,13 +7,13 @@
|
||||
|
||||
[section:FieldSequence FieldSequence requirements]
|
||||
|
||||
A [*`FieldSequence`] is an iterable container whose value type meets
|
||||
the requirements of [link beast.ref.Field [*`Field`]]. Objects meeting
|
||||
these requirements are serializable.
|
||||
A [*FieldSequence] is an iterable container whose value type meets
|
||||
the requirements of [link beast.ref.Field [*Field]]. Objects that meet
|
||||
these requirements become serializable by the implementation.
|
||||
|
||||
In this table:
|
||||
|
||||
* `X` denotes a type that meets the requirements of [*`FieldSequence`].
|
||||
* `X` denotes a type that meets the requirements of [*FieldSequence].
|
||||
|
||||
* `a` is a value of type `X`.
|
||||
|
||||
@@ -23,14 +23,14 @@ In this table:
|
||||
[`X::value_type`]
|
||||
[]
|
||||
[
|
||||
A type that meets the requirements of `Field`.
|
||||
A type that meets the requirements of @b Field.
|
||||
]
|
||||
]
|
||||
[
|
||||
[`X::const_iterator`]
|
||||
[]
|
||||
[
|
||||
A type that meets the requirements of `ForwardIterator`.
|
||||
A type that meets the requirements of @b ForwardIterator.
|
||||
]
|
||||
]
|
||||
[
|
||||
|
@@ -7,17 +7,20 @@
|
||||
|
||||
[section:Parser Parser requirements]
|
||||
|
||||
A [*`Parser`] is used to deserialize HTTP/1 messages from [link beast.ref.streams streams].
|
||||
Objects of this type are used with [link beast.ref.http__parse http::parse] and
|
||||
[link beast.ref.http__async_parse http::async_parse].
|
||||
A [*Parser] is used to deserialize objects from
|
||||
[link beast.ref.streams streams]. Objects of this type are used with
|
||||
[link beast.ref.http__parse http::parse] and
|
||||
[link beast.ref.http__async_parse http::async_parse]. The definition of
|
||||
an object, and the predicate defining when the parse is complete, are
|
||||
determined by the implementation.
|
||||
|
||||
In this table:
|
||||
|
||||
* `X` denotes a type meeting the requirements of [*`Parser`].
|
||||
* `X` denotes a type meeting the requirements of [*Parser].
|
||||
|
||||
* `a` denotes a value of type `X`.
|
||||
|
||||
* `b` is a value meeting the requirements of [@http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/ConstBufferSequence.html [*`ConstBufferSequence`]].
|
||||
* `b` is a value meeting the requirements of __ConstBufferSequence__.
|
||||
|
||||
* `ec` is a value of type [link beast.ref.error_code `error_code&`].
|
||||
|
||||
@@ -27,18 +30,18 @@ In this table:
|
||||
[`a.complete()`]
|
||||
[`bool`]
|
||||
[
|
||||
Returns `true` when a complete HTTP/1 message has been parsed.
|
||||
Returns `true` when the parser is complete.
|
||||
]
|
||||
]
|
||||
[
|
||||
[`a.write(b, ec)`]
|
||||
[`std::size_t`]
|
||||
[
|
||||
Sequentially parses the octets in the specified input buffer or input
|
||||
buffer sequence until an error occurs, the end of the buffer is reached,
|
||||
or a complete HTTP/1 message has been parsed. If an error occurs, `ec`
|
||||
is set to the error code and parsing stops. Upon success, this function
|
||||
returns the number of bytes used from the input.
|
||||
Sequentially parses the octets in the specified input buffer sequence
|
||||
until an error occurs, the end of the buffer is reached, or parsing is
|
||||
complete. Upon success, this function returns the number of bytes used
|
||||
from the input. If an error occurs, `ec` is set to the error code and
|
||||
parsing stops.
|
||||
]
|
||||
]
|
||||
[
|
||||
@@ -48,9 +51,9 @@ In this table:
|
||||
Indicates to the parser that no more octets will be available.
|
||||
Typically this function is called when the end of stream is reached.
|
||||
For example, if a call to `boost::asio::ip::tcp::socket::read_some`
|
||||
generates a `boost::asio::error::eof` error. Some HTTP/1 messages
|
||||
determine the end of the message body by an end of file marker or
|
||||
closing of the connection.
|
||||
generates a `boost::asio::error::eof` error. Some objects, such as
|
||||
certain HTTP/1 messages, determine the end of the message body by
|
||||
an end of file marker or closing of the connection.
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@@ -7,25 +7,24 @@
|
||||
|
||||
[section:Reader Reader requirements]
|
||||
|
||||
Parser implementations will construct the corresponding `reader` object
|
||||
during the parse. This customization point allows the Body to determine
|
||||
the strategy for storing incoming message body data.
|
||||
Parsers provided by the implementation will construct the corresponding
|
||||
`reader` object during the parse. This customization point allows the
|
||||
Body to determine the strategy for storing incoming message body data.
|
||||
|
||||
In this table:
|
||||
|
||||
* `X` denotes a type meeting the requirements of [*`Reader`].
|
||||
* `X` denotes a type meeting the requirements of [*`Reader`]
|
||||
|
||||
* `a` denotes a value of type `X`.
|
||||
* `a` denotes a value of type `X`
|
||||
|
||||
* `p` is any pointer.
|
||||
* `p` is any pointer
|
||||
|
||||
* `n` is a value convertible to `std::size_t`.
|
||||
* `n` is a value convertible to `std::size_t`
|
||||
|
||||
* `ec` is a value of type `error_code&`.
|
||||
* `ec` is a value of type [link beast.ref.error_code `error_code&`]
|
||||
|
||||
* `m` denotes a value of type `message const&` where
|
||||
`std::is_same<decltype(m.body), Body::value_type>:value == true`
|
||||
|
||||
`std::is_same<decltype(m.body), Body::value_type>::value == true`
|
||||
|
||||
[table Reader requirements
|
||||
[[operation] [type] [semantics, pre/post-conditions]]
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include <beast/http/headers.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/message_v1.hpp>
|
||||
#include <beast/http/parse.hpp>
|
||||
#include <beast/http/parse_error.hpp>
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
|
287
include/beast/http/impl/parse.ipp
Normal file
287
include/beast/http/impl/parse.ipp
Normal file
@@ -0,0 +1,287 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 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_alloc.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
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
Stream& s;
|
||||
DynamicBuffer& db;
|
||||
Parser& p;
|
||||
Handler h;
|
||||
bool started = false;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, Stream& s_,
|
||||
DynamicBuffer& sb_, Parser& p_)
|
||||
: s(s_)
|
||||
, db(sb_)
|
||||
, p(p_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
parse_op(parse_op&&) = default;
|
||||
parse_op(parse_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
parse_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), s,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, parse_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, parse_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(parse_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, parse_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream,
|
||||
class 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:
|
||||
{
|
||||
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.started = 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;
|
||||
}
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
// read
|
||||
d.state = 2;
|
||||
d.s.async_read_some(d.db.prepare(
|
||||
read_size_helper(d.db, 65536)),
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got data
|
||||
case 2:
|
||||
{
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
if(! d.started)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
// Caller will see eof on next read.
|
||||
ec = {};
|
||||
d.p.write_eof(ec);
|
||||
BOOST_ASSERT(ec || d.p.complete());
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
d.db.commit(bytes_transferred);
|
||||
auto const used = d.p.write(d.db.data(), ec);
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(used > 0)
|
||||
d.started = true;
|
||||
d.db.consume(used);
|
||||
if(d.p.complete())
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
d.h(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 started = false;
|
||||
for(;;)
|
||||
{
|
||||
auto used =
|
||||
parser.write(dynabuf.data(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
dynabuf.consume(used);
|
||||
if(used > 0)
|
||||
started = 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(! started)
|
||||
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
|
@@ -9,6 +9,7 @@
|
||||
#define BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||
|
||||
#include <beast/http/concepts.hpp>
|
||||
#include <beast/http/parse.hpp>
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/handler_alloc.hpp>
|
||||
@@ -20,185 +21,6 @@ namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class Stream,
|
||||
class DynamicBuffer, class Parser, class Handler>
|
||||
class parse_op
|
||||
{
|
||||
using alloc_type =
|
||||
handler_alloc<char, Handler>;
|
||||
|
||||
struct data
|
||||
{
|
||||
Stream& s;
|
||||
DynamicBuffer& db;
|
||||
Parser& p;
|
||||
Handler h;
|
||||
bool started = false;
|
||||
bool cont;
|
||||
int state = 0;
|
||||
|
||||
template<class DeducedHandler>
|
||||
data(DeducedHandler&& h_, Stream& s_,
|
||||
DynamicBuffer& sb_, Parser& p_)
|
||||
: s(s_)
|
||||
, db(sb_)
|
||||
, p(p_)
|
||||
, h(std::forward<DeducedHandler>(h_))
|
||||
, cont(boost_asio_handler_cont_helpers::
|
||||
is_continuation(h))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<data> d_;
|
||||
|
||||
public:
|
||||
parse_op(parse_op&&) = default;
|
||||
parse_op(parse_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
parse_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||
std::forward<DeducedHandler>(h), s,
|
||||
std::forward<Args>(args)...))
|
||||
{
|
||||
(*this)(error_code{}, 0, false);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred, bool again = true);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, parse_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
allocate(size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, parse_op* op)
|
||||
{
|
||||
return boost_asio_handler_alloc_helpers::
|
||||
deallocate(p, size, op->d_->h);
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(parse_op* op)
|
||||
{
|
||||
return op->d_->cont;
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, parse_op* op)
|
||||
{
|
||||
return boost_asio_handler_invoke_helpers::
|
||||
invoke(f, op->d_->h);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream,
|
||||
class 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:
|
||||
{
|
||||
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.started = 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;
|
||||
}
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
// read
|
||||
d.state = 2;
|
||||
d.s.async_read_some(d.db.prepare(
|
||||
read_size_helper(d.db, 65536)),
|
||||
std::move(*this));
|
||||
return;
|
||||
|
||||
// got data
|
||||
case 2:
|
||||
{
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
if(! d.started)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
// Caller will see eof on next read.
|
||||
ec = {};
|
||||
d.p.write_eof(ec);
|
||||
BOOST_ASSERT(ec || d.p.complete());
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
d.db.commit(bytes_transferred);
|
||||
auto const used = d.p.write(d.db.data(), ec);
|
||||
if(ec)
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
if(used > 0)
|
||||
d.started = true;
|
||||
d.db.consume(used);
|
||||
if(d.p.complete())
|
||||
{
|
||||
// call handler
|
||||
d.state = 99;
|
||||
break;
|
||||
}
|
||||
d.state = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
d.h(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Headers,
|
||||
class Handler>
|
||||
@@ -318,87 +140,6 @@ operator()(error_code ec, bool again)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
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 started = false;
|
||||
for(;;)
|
||||
{
|
||||
auto used =
|
||||
parser.write(dynabuf.data(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
dynabuf.consume(used);
|
||||
if(used > 0)
|
||||
started = 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(! started)
|
||||
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();
|
||||
}
|
||||
|
||||
template<class SyncReadStream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Headers>
|
||||
void
|
||||
|
152
include/beast/http/parse.hpp
Normal file
152
include/beast/http/parse.hpp
Normal file
@@ -0,0 +1,152 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef BEAST_HTTP_PARSE_HPP
|
||||
#define BEAST_HTTP_PARSE_HPP
|
||||
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/async_completion.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Parse an object from a stream.
|
||||
|
||||
This function synchronously reads from a stream and passes
|
||||
data to the specified parser. The call will block until one
|
||||
of the following conditions are met:
|
||||
|
||||
@li The parser indicates that parsing is complete.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function is implemented in terms of one or more calls
|
||||
to the stream's `read_some` function. The implementation may
|
||||
read additional octets that lie past the end of the object
|
||||
being parsed. This additional data is stored in the stream
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@note This algorithm is generic, and not specific to HTTP
|
||||
messages. It is up to the parser to determine what predicate
|
||||
defines a complete operation.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param dynabuf A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser An object meeting the requirements of @b Parser
|
||||
which will receive the data.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf, Parser& parser);
|
||||
|
||||
/** Parse an object from a stream.
|
||||
|
||||
This function synchronously reads from a stream and passes
|
||||
data to the specified parser. The call will block until one
|
||||
of the following conditions are met:
|
||||
|
||||
@li The parser indicates that parsing is complete.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function is implemented in terms of one or more calls
|
||||
to the stream's `read_some` function. The implementation may
|
||||
read additional octets that lie past the end of the object
|
||||
being parsed. This additional data is stored in the stream
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@note This algorithm is generic, and not specific to HTTP
|
||||
messages. It is up to the parser to determine what predicate
|
||||
defines a complete operation.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param dynabuf A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser An object meeting the requirements of @b Parser
|
||||
which will receive the data.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf, Parser& parser, error_code& ec);
|
||||
|
||||
/** Start an asynchronous operation to parse an object from a stream.
|
||||
|
||||
This function is used to asynchronously read from a stream and
|
||||
pass the data to the specified parser. The function call always
|
||||
returns immediately. The asynchronous operation will continue
|
||||
until one of the following conditions is true:
|
||||
|
||||
@li The parser indicates that parsing is complete.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This operation is implemented in terms of one or more calls to
|
||||
the next layer's `async_read_some` function, and is known as a
|
||||
<em>composed operation</em>. The program must ensure that the
|
||||
stream performs no other operations until this operation
|
||||
completes.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b AsyncReadStream concept.
|
||||
|
||||
@param dynabuf A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser An object meeting the requirements of @b Parser
|
||||
which will receive the data. This object must remain valid
|
||||
until the completion handler is invoked.
|
||||
|
||||
@param handler The handler to be called when the request
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
*/
|
||||
template<class AsyncReadStream,
|
||||
class DynamicBuffer, class Parser, class ReadHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_parse(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
Parser& parser, ReadHandler&& handler);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/parse.ipp>
|
||||
|
||||
#endif
|
@@ -8,135 +8,13 @@
|
||||
#ifndef BEAST_HTTP_READ_HPP
|
||||
#define BEAST_HTTP_READ_HPP
|
||||
|
||||
#include <beast/http/parser_v1.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/async_completion.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/message_v1.hpp>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Parse a HTTP/1 message from a stream.
|
||||
|
||||
This function synchronously reads from a stream and passes
|
||||
data to the specified parser. The call will block until one
|
||||
of the following conditions are met:
|
||||
|
||||
@li A complete message is read in.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function is implemented in terms of one or more calls
|
||||
to the stream's `read_some` function. The implementation may
|
||||
read additional octets that lie past the end of the message
|
||||
being parsed. This additional data is stored in the stream
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b `SyncReadStream` concept.
|
||||
|
||||
@param dynabuf A @b `DynamicBuffer` holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser An object meeting the requirements of Parser
|
||||
which will receive the data.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf, Parser& parser);
|
||||
|
||||
/** Parse a HTTP/1 message from a stream.
|
||||
|
||||
This function synchronously reads from a stream and passes
|
||||
data to the specified parser. The call will block until one
|
||||
of the following conditions are met:
|
||||
|
||||
@li A complete message is read in.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function is implemented in terms of one or more calls
|
||||
to the stream's `read_some` function. The implementation may
|
||||
read additional octets that lie past the end of the message
|
||||
being parsed. This additional data is stored in the stream
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b `SyncReadStream` concept.
|
||||
|
||||
@param dynabuf A @b `DynamicBuffer` holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser An object meeting the requirements of `Parser`
|
||||
which will receive the data.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
template<class SyncReadStream, class DynamicBuffer, class Parser>
|
||||
void
|
||||
parse(SyncReadStream& stream,
|
||||
DynamicBuffer& dynabuf, Parser& parser, error_code& ec);
|
||||
|
||||
/** Start an asynchronous operation to parse a HTTP/1 message from a stream.
|
||||
|
||||
This function is used to asynchronously read from a stream and
|
||||
pass the data to the specified parser. The function call always
|
||||
returns immediately. The asynchronous operation will continue
|
||||
until one of the following conditions is true:
|
||||
|
||||
@li A complete message is read in.
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This operation is implemented in terms of one or more calls to
|
||||
the next layer's `async_read_some` function, and is known as a
|
||||
<em>composed operation</em>. The program must ensure that the
|
||||
stream performs no other operations until this operation completes.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b `AsyncReadStream` concept.
|
||||
|
||||
@param dynabuf A @b `DynamicBuffer` holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
stream buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser An object meeting the requirements of `Parser`
|
||||
which will receive the data. This object must remain valid
|
||||
until the completion handler is invoked.
|
||||
|
||||
@param handler The handler to be called when the request completes.
|
||||
Copies will be made of the handler as required. The equivalent
|
||||
function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
*/
|
||||
template<class AsyncReadStream,
|
||||
class DynamicBuffer, class Parser, class ReadHandler>
|
||||
#if GENERATING_DOCS
|
||||
void_or_deduced
|
||||
#else
|
||||
typename async_completion<
|
||||
ReadHandler, void(error_code)>::result_type
|
||||
#endif
|
||||
async_parse(AsyncReadStream& stream, DynamicBuffer& dynabuf,
|
||||
Parser& parser, ReadHandler&& handler);
|
||||
|
||||
/** Read a HTTP/1 message from a stream.
|
||||
|
||||
This function is used to synchronously read a message from
|
||||
|
@@ -50,6 +50,7 @@ unit-test http-tests :
|
||||
http/headers.cpp
|
||||
http/message.cpp
|
||||
http/message_v1.cpp
|
||||
http/parse.cpp
|
||||
http/parse_error.cpp
|
||||
http/parser_v1.cpp
|
||||
http/read.cpp
|
||||
|
@@ -19,6 +19,7 @@ add_executable (http-tests
|
||||
headers.cpp
|
||||
message.cpp
|
||||
message_v1.cpp
|
||||
parse.cpp
|
||||
parse_error.cpp
|
||||
parser_v1.cpp
|
||||
read.cpp
|
||||
|
9
test/http/parse.cpp
Normal file
9
test/http/parse.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
//
|
||||
// Copyright (c) 2013-2016 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)
|
||||
//
|
||||
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/http/parse.hpp>
|
Reference in New Issue
Block a user