forked from boostorg/beast
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
|
* Make value optional in param-list
|
||||||
* Frame processing routines are member functions
|
* Frame processing routines are member functions
|
||||||
* Fix on_headers called twice from basic_parser_v1
|
* Fix on_headers called twice from basic_parser_v1
|
||||||
|
* Refine Parser concept
|
||||||
|
|
||||||
API Changes:
|
API Changes:
|
||||||
|
|
||||||
|
@@ -103,7 +103,7 @@ maximum inlining and optimization.
|
|||||||
Boost.Asio is the inspiration behind which all of the interfaces and
|
Boost.Asio is the inspiration behind which all of the interfaces and
|
||||||
implementation strategies are built. Some parts of the documentation are
|
implementation strategies are built. Some parts of the documentation are
|
||||||
written to closely resemble the wording and presentation of Boost.Asio
|
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.
|
Asio library and the ideas upon which Beast is built.
|
||||||
|
|
||||||
Beast would not be possible without the considerable time and patience
|
Beast would not be possible without the considerable time and patience
|
||||||
|
@@ -7,13 +7,13 @@
|
|||||||
|
|
||||||
[section:FieldSequence FieldSequence requirements]
|
[section:FieldSequence FieldSequence requirements]
|
||||||
|
|
||||||
A [*`FieldSequence`] is an iterable container whose value type meets
|
A [*FieldSequence] is an iterable container whose value type meets
|
||||||
the requirements of [link beast.ref.Field [*`Field`]]. Objects meeting
|
the requirements of [link beast.ref.Field [*Field]]. Objects that meet
|
||||||
these requirements are serializable.
|
these requirements become serializable by the implementation.
|
||||||
|
|
||||||
In this table:
|
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`.
|
* `a` is a value of type `X`.
|
||||||
|
|
||||||
@@ -23,14 +23,14 @@ In this table:
|
|||||||
[`X::value_type`]
|
[`X::value_type`]
|
||||||
[]
|
[]
|
||||||
[
|
[
|
||||||
A type that meets the requirements of `Field`.
|
A type that meets the requirements of @b Field.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`X::const_iterator`]
|
[`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]
|
[section:Parser Parser requirements]
|
||||||
|
|
||||||
A [*`Parser`] is used to deserialize HTTP/1 messages from [link beast.ref.streams streams].
|
A [*Parser] is used to deserialize objects from
|
||||||
Objects of this type are used with [link beast.ref.http__parse http::parse] and
|
[link beast.ref.streams streams]. Objects of this type are used with
|
||||||
[link beast.ref.http__async_parse http::async_parse].
|
[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:
|
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`.
|
* `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&`].
|
* `ec` is a value of type [link beast.ref.error_code `error_code&`].
|
||||||
|
|
||||||
@@ -27,18 +30,18 @@ In this table:
|
|||||||
[`a.complete()`]
|
[`a.complete()`]
|
||||||
[`bool`]
|
[`bool`]
|
||||||
[
|
[
|
||||||
Returns `true` when a complete HTTP/1 message has been parsed.
|
Returns `true` when the parser is complete.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
[`a.write(b, ec)`]
|
[`a.write(b, ec)`]
|
||||||
[`std::size_t`]
|
[`std::size_t`]
|
||||||
[
|
[
|
||||||
Sequentially parses the octets in the specified input buffer or input
|
Sequentially parses the octets in the specified input buffer sequence
|
||||||
buffer sequence until an error occurs, the end of the buffer is reached,
|
until an error occurs, the end of the buffer is reached, or parsing is
|
||||||
or a complete HTTP/1 message has been parsed. If an error occurs, `ec`
|
complete. Upon success, this function returns the number of bytes used
|
||||||
is set to the error code and parsing stops. Upon success, this function
|
from the input. If an error occurs, `ec` is set to the error code and
|
||||||
returns the number of bytes used from the input.
|
parsing stops.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
[
|
[
|
||||||
@@ -48,9 +51,9 @@ In this table:
|
|||||||
Indicates to the parser that no more octets will be available.
|
Indicates to the parser that no more octets will be available.
|
||||||
Typically this function is called when the end of stream is reached.
|
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`
|
For example, if a call to `boost::asio::ip::tcp::socket::read_some`
|
||||||
generates a `boost::asio::error::eof` error. Some HTTP/1 messages
|
generates a `boost::asio::error::eof` error. Some objects, such as
|
||||||
determine the end of the message body by an end of file marker or
|
certain HTTP/1 messages, determine the end of the message body by
|
||||||
closing of the connection.
|
an end of file marker or closing of the connection.
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@@ -7,25 +7,24 @@
|
|||||||
|
|
||||||
[section:Reader Reader requirements]
|
[section:Reader Reader requirements]
|
||||||
|
|
||||||
Parser implementations will construct the corresponding `reader` object
|
Parsers provided by the implementation will construct the corresponding
|
||||||
during the parse. This customization point allows the Body to determine
|
`reader` object during the parse. This customization point allows the
|
||||||
the strategy for storing incoming message body data.
|
Body to determine the strategy for storing incoming message body data.
|
||||||
|
|
||||||
In this table:
|
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
|
* `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
|
[table Reader requirements
|
||||||
[[operation] [type] [semantics, pre/post-conditions]]
|
[[operation] [type] [semantics, pre/post-conditions]]
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
#include <beast/http/headers.hpp>
|
#include <beast/http/headers.hpp>
|
||||||
#include <beast/http/message.hpp>
|
#include <beast/http/message.hpp>
|
||||||
#include <beast/http/message_v1.hpp>
|
#include <beast/http/message_v1.hpp>
|
||||||
|
#include <beast/http/parse.hpp>
|
||||||
#include <beast/http/parse_error.hpp>
|
#include <beast/http/parse_error.hpp>
|
||||||
#include <beast/http/parser_v1.hpp>
|
#include <beast/http/parser_v1.hpp>
|
||||||
#include <beast/http/read.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
|
#define BEAST_HTTP_IMPL_READ_IPP_HPP
|
||||||
|
|
||||||
#include <beast/http/concepts.hpp>
|
#include <beast/http/concepts.hpp>
|
||||||
|
#include <beast/http/parse.hpp>
|
||||||
#include <beast/http/parser_v1.hpp>
|
#include <beast/http/parser_v1.hpp>
|
||||||
#include <beast/core/bind_handler.hpp>
|
#include <beast/core/bind_handler.hpp>
|
||||||
#include <beast/core/handler_alloc.hpp>
|
#include <beast/core/handler_alloc.hpp>
|
||||||
@@ -20,185 +21,6 @@ namespace http {
|
|||||||
|
|
||||||
namespace detail {
|
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,
|
template<class Stream, class DynamicBuffer,
|
||||||
bool isRequest, class Body, class Headers,
|
bool isRequest, class Body, class Headers,
|
||||||
class Handler>
|
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,
|
template<class SyncReadStream, class DynamicBuffer,
|
||||||
bool isRequest, class Body, class Headers>
|
bool isRequest, class Body, class Headers>
|
||||||
void
|
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
|
#ifndef BEAST_HTTP_READ_HPP
|
||||||
#define 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 <beast/core/async_completion.hpp>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <beast/core/error.hpp>
|
||||||
|
#include <beast/http/message_v1.hpp>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
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.
|
/** Read a HTTP/1 message from a stream.
|
||||||
|
|
||||||
This function is used to synchronously read a message from
|
This function is used to synchronously read a message from
|
||||||
|
@@ -50,6 +50,7 @@ unit-test http-tests :
|
|||||||
http/headers.cpp
|
http/headers.cpp
|
||||||
http/message.cpp
|
http/message.cpp
|
||||||
http/message_v1.cpp
|
http/message_v1.cpp
|
||||||
|
http/parse.cpp
|
||||||
http/parse_error.cpp
|
http/parse_error.cpp
|
||||||
http/parser_v1.cpp
|
http/parser_v1.cpp
|
||||||
http/read.cpp
|
http/read.cpp
|
||||||
|
@@ -19,6 +19,7 @@ add_executable (http-tests
|
|||||||
headers.cpp
|
headers.cpp
|
||||||
message.cpp
|
message.cpp
|
||||||
message_v1.cpp
|
message_v1.cpp
|
||||||
|
parse.cpp
|
||||||
parse_error.cpp
|
parse_error.cpp
|
||||||
parser_v1.cpp
|
parser_v1.cpp
|
||||||
read.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