Refactor HTTP serialization and parsing (API Change):

fix #404

Parsing and serialization interfaces have been fine tuned and unified.

For parsing these stream operations are defined:

* read
* read_header
* read_some
* async_read
* async_read_header
* async_read_some

For serialization these stream operations are defined:

* write
* write_header
* write_some
* async_write
* async_write_header
* async_write_some
This commit is contained in:
Vinnie Falco
2017-05-31 08:01:55 -07:00
parent b76eb2864e
commit 02d5aedc29
44 changed files with 3655 additions and 3250 deletions

View File

@ -7,6 +7,7 @@ API Changes:
* Refactor treatment of request-method
* Refactor treatment of status code and obsolete reason
* Refactor HTTP serialization and parsing
--------------------------------------------------------------------------------

View File

@ -188,6 +188,7 @@ int main()
Example HTTP program:
```C++
#include <beast/core.hpp>
#include <beast/http.hpp>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>
@ -197,7 +198,7 @@ Example HTTP program:
int main()
{
// Normal boost::asio setup
std::string const host = "boost.org";
std::string const host = "www.example.com";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
@ -206,7 +207,7 @@ int main()
// Send HTTP request using beast
beast::http::request<beast::http::string_body> req;
req.method("GET");
req.method(beast::http::verb::get);
req.target("/");
req.version = 11;
req.fields.replace("Host", host + ":" +
@ -216,10 +217,10 @@ int main()
beast::http::write(sock, req);
// Receive and print HTTP response using beast
beast::streambuf sb;
beast::http::response<beast::http::dynamic_body> resp;
beast::http::read(sock, sb, resp);
std::cout << resp;
beast::flat_buffer b;
beast::http::response<beast::http::dynamic_body> res;
beast::http::read(sock, b, res);
std::cout << res << std::endl;
}
```

View File

@ -144,11 +144,21 @@ and which must remain valid until the operation is complete:
][
Send __serializer__ buffer data to a __SyncWriteStream__.
]]
[[
[link beast.ref.http__write_header.overload1 [*write_header]]
][
Send an entire header from a __serializer__ to a __SyncWriteStream__.
]]
[[
[link beast.ref.http__async_write_some [*async_write_some]]
][
Send some __serializer__ buffer data to an __AsyncWriteStream__.
]]
[[
[link beast.ref.http__async_write_header [*async_write_header]]
][
Send an entire header from a __serializer__ to a __AsyncWriteStream__.
]]
]
Here is an example which synchronously sends a message on a stream using

View File

@ -27,7 +27,7 @@ Use HTTP to request the root page from a website and print the response:
int main()
{
// Normal boost::asio setup
std::string const host = "boost.org";
std::string const host = "www.example.com";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
@ -49,7 +49,7 @@ int main()
beast::flat_buffer b;
beast::http::response<beast::http::dynamic_body> res;
beast::http::read(sock, b, res);
std::cout << res;
std::cout << res << std::endl;
}
```
[heading WebSocket]

View File

@ -59,8 +59,10 @@
<bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__async_read">async_read</link></member>
<member><link linkend="beast.ref.http__async_read_header">async_read_header</link></member>
<member><link linkend="beast.ref.http__async_read_some">async_read_some</link></member>
<member><link linkend="beast.ref.http__async_write">async_write</link></member>
<member><link linkend="beast.ref.http__async_write_header">async_write_header</link></member>
<member><link linkend="beast.ref.http__async_write_some">async_write_some</link></member>
<member><link linkend="beast.ref.http__is_keep_alive">is_keep_alive</link></member>
<member><link linkend="beast.ref.http__is_upgrade">is_upgrade</link></member>
@ -69,19 +71,15 @@
<member><link linkend="beast.ref.http__operator_ls_">operator&lt;&lt;</link></member>
<member><link linkend="beast.ref.http__prepare">prepare</link></member>
<member><link linkend="beast.ref.http__read">read</link></member>
<member><link linkend="beast.ref.http__read_header">read_header</link></member>
<member><link linkend="beast.ref.http__read_some">read_some</link></member>
<member><link linkend="beast.ref.http__string_to_verb">string_to_verb</link></member>
<member><link linkend="beast.ref.http__swap">swap</link></member>
<member><link linkend="beast.ref.http__to_string">to_string</link></member>
<member><link linkend="beast.ref.http__write">write</link></member>
<member><link linkend="beast.ref.http__write_header">write_header</link></member>
<member><link linkend="beast.ref.http__write_some">write_some</link></member>
</simplelist>
<bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__is_body">is_body</link></member>
<member><link linkend="beast.ref.http__is_body_writer">is_body_writer</link></member>
<member><link linkend="beast.ref.http__is_body_reader">is_body_reader</link></member>
</simplelist>
</entry>
<entry valign="top">
<bridgehead renderas="sect3">Constants</bridgehead>
@ -91,6 +89,12 @@
<member><link linkend="beast.ref.http__status">status</link></member>
<member><link linkend="beast.ref.http__verb">verb</link></member>
</simplelist>
<bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.http__is_body">is_body</link></member>
<member><link linkend="beast.ref.http__is_body_writer">is_body_writer</link></member>
<member><link linkend="beast.ref.http__is_body_reader">is_body_reader</link></member>
</simplelist>
<bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1">
<member><link linkend="beast.ref.Body">Body</link></member>

View File

@ -15,7 +15,7 @@
int main()
{
// Normal boost::asio setup
std::string const host = "boost.org";
std::string const host = "www.example.com";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
@ -37,5 +37,5 @@ int main()
beast::flat_buffer b;
beast::http::response<beast::http::dynamic_body> res;
beast::http::read(sock, b, res);
std::cout << res;
std::cout << res << std::endl;
}

View File

@ -55,6 +55,7 @@ private:
buffer_type b;
std::condition_variable cv;
std::unique_ptr<read_op> op;
bool eof = false;
};
state s_[2];
@ -151,6 +152,15 @@ public:
std::size_t>::max)());
}
/** Close the stream.
The other end of the pipe will see
`boost::asio::error::eof` on read.
*/
template<class = void>
void
close();
template<class MutableBufferSequence>
std::size_t
read_some(MutableBufferSequence const& buffers);
@ -275,22 +285,49 @@ read_op_impl<Handler, Buffers>::operator()()
{
BOOST_ASSERT(s_.in_.op);
std::unique_lock<std::mutex> lock{s_.in_.m};
BOOST_ASSERT(buffer_size(s_.in_.b.data()) > 0);
auto const bytes_transferred = buffer_copy(
b_, s_.in_.b.data(), s_.read_max_);
s_.in_.b.consume(bytes_transferred);
auto& s = s_;
Handler h{std::move(h_)};
lock.unlock();
s.in_.op.reset(nullptr);
++s.nread;
s.ios_.post(bind_handler(std::move(h),
error_code{}, bytes_transferred));
if(s_.in_.b.size() > 0)
{
auto const bytes_transferred = buffer_copy(
b_, s_.in_.b.data(), s_.read_max_);
s_.in_.b.consume(bytes_transferred);
auto& s = s_;
Handler h{std::move(h_)};
lock.unlock();
s.in_.op.reset(nullptr);
++s.nread;
s.ios_.post(bind_handler(std::move(h),
error_code{}, bytes_transferred));
}
else
{
BOOST_ASSERT(s_.in_.eof);
auto& s = s_;
Handler h{std::move(h_)};
lock.unlock();
s.in_.op.reset(nullptr);
++s.nread;
s.ios_.post(bind_handler(std::move(h),
boost::asio::error::eof, 0));
}
});
}
//------------------------------------------------------------------------------
template<class>
void
pipe::stream::
close()
{
std::lock_guard<std::mutex> lock{out_.m};
out_.eof = true;
if(out_.op)
out_.op.get()->operator()();
else
out_.cv.notify_all();
}
template<class MutableBufferSequence>
std::size_t
pipe::stream::
@ -318,19 +355,28 @@ read_some(MutableBufferSequence const& buffers,
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(! in_.op);
BOOST_ASSERT(buffer_size(buffers) > 0);
if(fc_ && fc_->fail(ec))
return 0;
std::unique_lock<std::mutex> lock{in_.m};
in_.cv.wait(lock,
[&]()
{
return
buffer_size(buffers) == 0 ||
buffer_size(in_.b.data()) > 0;
return in_.b.size() > 0 || in_.eof;
});
auto const bytes_transferred = buffer_copy(
buffers, in_.b.data(), write_max_);
in_.b.consume(bytes_transferred);
std::size_t bytes_transferred;
if(in_.b.size() > 0)
{
bytes_transferred = buffer_copy(
buffers, in_.b.data(), write_max_);
in_.b.consume(bytes_transferred);
}
else
{
BOOST_ASSERT(in_.eof);
bytes_transferred = 0;
ec = boost::asio::error::eof;
}
++nread;
return bytes_transferred;
}
@ -347,9 +393,10 @@ async_read_some(MutableBufferSequence const& buffers,
"MutableBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(! in_.op);
BOOST_ASSERT(buffer_size(buffers) > 0);
async_completion<ReadHandler,
void(error_code, std::size_t)> init{handler};
BOOST_ASSERT(! in_.op);
if(fc_)
{
error_code ec;
@ -359,7 +406,14 @@ async_read_some(MutableBufferSequence const& buffers,
}
{
std::unique_lock<std::mutex> lock{in_.m};
if(buffer_size(buffers) == 0 ||
if(in_.eof)
{
lock.unlock();
++nread;
ios_.post(bind_handler(init.completion_handler,
boost::asio::error::eof, 0));
}
else if(buffer_size(buffers) == 0 ||
buffer_size(in_.b.data()) > 0)
{
auto const bytes_transferred = buffer_copy(
@ -389,6 +443,7 @@ write_some(ConstBufferSequence const& buffers)
static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
BOOST_ASSERT(! out_.eof);
error_code ec;
auto const bytes_transferred =
write_some(buffers, ec);
@ -408,6 +463,7 @@ write_some(
"ConstBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(! out_.eof);
if(fc_ && fc_->fail(ec))
return 0;
auto const n = (std::min)(
@ -437,6 +493,7 @@ async_write_some(ConstBufferSequence const& buffers,
"ConstBufferSequence requirements not met");
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
BOOST_ASSERT(! out_.eof);
async_completion<WriteHandler,
void(error_code, std::size_t)> init{handler};
if(fc_)

View File

@ -15,6 +15,7 @@
#include <functional>
#include <mutex>
#include <thread>
#include <vector>
namespace beast {
namespace test {
@ -32,7 +33,7 @@ protected:
private:
boost::optional<boost::asio::io_service::work> work_;
std::thread thread_;
std::vector<std::thread> threads_;
std::mutex m_;
std::condition_variable cv_;
std::size_t running_ = 0;
@ -42,20 +43,21 @@ public:
using yield_context =
boost::asio::yield_context;
enable_yield_to()
explicit
enable_yield_to(std::size_t concurrency = 1)
: work_(ios_)
, thread_([&]
{
ios_.run();
}
)
{
threads_.reserve(concurrency);
while(concurrency--)
threads_.emplace_back(
[&]{ ios_.run(); });
}
~enable_yield_to()
{
work_ = boost::none;
thread_.join();
for(auto& t : threads_)
t.join();
}
/// Return the `io_service` associated with the object

View File

@ -10,6 +10,7 @@
#include <beast/core/type_traits.hpp>
#include <boost/assert.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
#include <cstddef>
@ -24,9 +25,13 @@ read_size_helper(DynamicBuffer const& buffer, std::size_t max_size)
"DynamicBuffer requirements not met");
BOOST_ASSERT(max_size >= 1);
auto const size = buffer.size();
return std::min<std::size_t>(
std::max<std::size_t>(512, buffer.capacity() - size),
std::min<std::size_t>(max_size, buffer.max_size() - size));
auto const limit = buffer.max_size() - size;
if(limit > 0)
return std::min<std::size_t>(
std::max<std::size_t>(512, buffer.capacity() - size),
std::min<std::size_t>(max_size, limit));
BOOST_THROW_EXCEPTION(std::length_error{
"dynamic buffer overflow"});
}
} // detail

View File

@ -80,7 +80,7 @@ write_some(ConstBufferSequence const& buffers,
buffer_size(buffer));
if(os_.fail())
{
ec = errc::make_error_code(
ec = make_error_code(
errc::no_stream_resources);
break;
}

View File

@ -21,41 +21,6 @@
namespace beast {
namespace http {
/** Describes the parser's current state.
The state is expressed as the type of data that
@ref basic_parser is expecting to see in subsequently
provided octets.
*/
enum class parse_state
{
/// Expecting one or more header octets
header = 0,
/// Expecting one or more body octets
body = 1,
/// Expecting zero or more body octets followed by EOF
body_to_eof = 2,
/// Expecting additional chunk header octets
chunk_header = 3,
/// Expecting one or more chunk body octets
chunk_body = 4,
/** The parsing is complete.
The parse is considered complete when the full header
is received and either the full body is received, or
the semantics of the message indicate that no body
is expected. This includes the case where the caller
has indicated to the parser that no body is expected,
for example when receiving a response to a HEAD request.
*/
complete = 5
};
/** A parser for decoding HTTP/1 wire format messages.
This parser is designed to efficiently parse messages in the
@ -83,18 +48,13 @@ enum class parse_state
struct derived
: basic_parser<isRequest, derived<isRequest>>
{
// The type used when providing a mutable
// buffer sequence in which to store body data.
//
using mutable_buffers_type = ...;
// When isRequest == true, called
// after the Request Line is received.
//
void
on_request(
string_view const& method,
string_view const& target,
string_view method,
string_view target,
int version,
error_code& ec);
@ -104,7 +64,7 @@ enum class parse_state
void
on_response(
int status,
string_view const& reason,
string_view reason,
int version,
error_code& ec);
@ -112,8 +72,8 @@ enum class parse_state
//
void
on_field(
string_view const& name,
string_view const& value,
string_view name,
string_view value,
error_code& ec);
// Called after the header is complete.
@ -123,40 +83,19 @@ enum class parse_state
error_code& ec);
// Called once before the body, if any, is started.
// This will only be called if the semantics of the
// message indicate that a body exists, including
// an indicated body of zero length.
//
void
on_body();
on_body(
boost::optional<std::uint64_t> content_length,
error_code& ec);
// Called zero or more times to provide body data.
//
// Only used if isDirect == false
//
void
on_data(
string_view const& s,
string_view s,
error_code& ec);
// Called zero or more times to retrieve a mutable
// buffer sequence in which to store body data.
//
// Only used if isDirect == true
//
mutable_buffers_type
on_prepare(
std::size_t n);
// Called after body data has been stored in the
// buffer returned by the previous call to on_prepare.
//
// Only used if isDirect == true
//
void
on_commit(
std::size_t n);
// If the Transfer-Encoding is specified, and the
// last item in the list of encodings is "chunked",
// called after receiving a chunk header or a final
@ -164,8 +103,8 @@ enum class parse_state
//
void
on_chunk(
std::uint64_t length, // Length of this chunk
string_view const& ext, // The chunk extensions, if any
std::uint64_t length, // Length of this chunk
string_view const& ext, // The chunk extensions, if any
error_code& ec);
// Called once when the message is complete.
@ -177,16 +116,7 @@ enum class parse_state
@endcode
If a callback sets the error code, the error will be propagated
to the caller of the parser. Behavior of parsing after an error
is returned is undefined.
When the parser state is positioned to read bytes belonging to
the body, calling @ref write or @ref write will implicitly
cause a buffer copy (because bytes are first transferred to the
dynamic buffer). To avoid this copy, the additional functions
@ref copy_body, @ref prepare_body, and @ref commit_body are
provided to allow the caller to read bytes directly into buffers
supplied by the parser.
to the caller of the parser.
The parser is optimized for the case where the input buffer
sequence consists of a single contiguous buffer. The
@ -203,26 +133,21 @@ enum class parse_state
@tparam isRequest A `bool` indicating whether the parser will be
presented with request or response message.
@tparam isDirect A `bool` indicating whether the parser interface
supports reading body data directly into parser-provided buffers.
@tparam Derived The derived class type. This is part of the
Curiously Recurring Template Pattern interface.
*/
template<bool isRequest, bool isDirect, class Derived>
template<bool isRequest, class Derived>
class basic_parser
: private detail::basic_parser_base
{
template<bool OtherIsRequest,
bool OtherIsDirect, class OtherDerived>
template<bool OtherIsRequest, class OtherDerived>
friend class basic_parser;
// Message will be complete after reading header
static unsigned constexpr flagSkipBody = 1<< 0;
static unsigned constexpr flagOnBody = 1<< 1;
// Consume input buffers across semantic boundaries
static unsigned constexpr flagEager = 1<< 1;
// The parser has read at least one byte
static unsigned constexpr flagGotSome = 1<< 2;
@ -248,10 +173,8 @@ class basic_parser
std::size_t buf_len_ = 0;
std::size_t skip_ = 0; // search from here
std::size_t x_; // scratch variable
state state_ = state::nothing_yet;
unsigned f_ = 0; // flags
parse_state state_ = parse_state::header;
string_view ext_;
string_view body_;
public:
/// Copy constructor (disallowed)
@ -264,7 +187,8 @@ public:
basic_parser() = default;
/// `true` if this parser parses requests, `false` for responses.
static bool constexpr is_request = isRequest;
using is_request =
std::integral_constant<bool, isRequest>;
/// Destructor
~basic_parser() = default;
@ -274,83 +198,70 @@ public:
After the move, the only valid operation on the
moved-from object is destruction.
*/
template<bool OtherIsDirect, class OtherDerived>
basic_parser(basic_parser<
isRequest, OtherIsDirect, OtherDerived>&&);
template<class OtherDerived>
basic_parser(basic_parser<isRequest, OtherDerived>&&);
/** Set the skip body option.
/** Returns a reference to this object as a @ref basic_parser.
The option controls whether or not the parser expects to
see an HTTP body, regardless of the presence or absence of
certain fields such as Content-Length.
Depending on the request, some responses do not carry a body.
For example, a 200 response to a CONNECT request from a
tunneling proxy. In these cases, callers may use this function
inform the parser that no body is expected. The parser will
consider the message complete after the header has been received.
@note This function must called before any bytes are processed.
This is used to pass a derived class where a base class is
expected, to choose a correct function overload when the
resolution would be ambiguous.
*/
void
skip_body();
/** Returns the current parser state.
The parser state indicates what octets the parser
expects to see next in the input stream.
*/
parse_state
state() const
basic_parser&
base()
{
return state_;
return *this;
}
/** Returns a constant reference to this object as a @ref basic_parser.
This is used to pass a derived class where a base class is
expected, to choose a correct function overload when the
resolution would be ambiguous.
*/
basic_parser const&
base() const
{
return *this;
}
/// Returns `true` if the parser has received at least one byte of input.
bool
got_some() const
{
return (f_ & flagGotSome) != 0;
}
/// Returns `true` if the complete header has been parsed.
bool
got_header() const
{
return state_ != parse_state::header;
}
/** Returns `true` if a Content-Length is specified.
@note Only valid after parsing a complete header.
*/
bool
got_content_length() const
{
return (f_ & flagContentLength) != 0;
return state_ != state::nothing_yet;
}
/** Returns `true` if the message is complete.
The message is complete after a full header is
parsed and one of the following is true:
The message is complete after the full header is prduced
and one of the following is true:
@li @ref skip_body was called
@li The skip body option was set.
@li The semantics of the message indicate there is no body.
@li The semantics of the message indicate a body is
expected, and the entire body was received.
@li The semantics of the message indicate a body is expected,
and the entire body was parsed.
*/
bool
is_complete() const
is_done() const
{
return state_ == parse_state::complete;
return state_ == state::complete;
}
/** Returns `true` if a the parser has produced the full header.
*/
bool
is_header_done() const
{
return state_ > state::header;
}
/** Returns `true` if the message is an upgrade message.
@note Only valid after parsing a complete header.
@note The return value is undefined unless
@ref is_header_done would return `true`.
*/
bool
is_upgrade() const
@ -358,16 +269,10 @@ public:
return (f_ & flagConnectionUpgrade) != 0;
}
/** Returns `true` if keep-alive is specified
/** Returns `true` if the last value for Transfer-Encoding is "chunked".
@note Only valid after parsing a complete header.
*/
bool
is_keep_alive() const;
/** Returns `true` if the chunked Transfer-Encoding is specified.
@note Only valid after parsing a complete header.
@note The return value is undefined unless
@ref is_header_done would return `true`.
*/
bool
is_chunked() const
@ -375,34 +280,125 @@ public:
return (f_ & flagChunked) != 0;
}
/** Write part of a buffer sequence to the parser.
/** Returns `true` if the message has keep-alive connection semantics.
This function attempts to parse the HTTP message
stored in the caller provided buffers. Upon success,
a positive return value indicates that the parser
@note The return value is undefined unless
@ref is_header_done would return `true`.
*/
bool
is_keep_alive() const;
/** Returns the optional value of Content-Length if known.
@note The return value is undefined unless
@ref is_header_done would return `true`.
*/
boost::optional<std::uint64_t>
content_length() const;
/** Returns `true` if the message semantics require an end of file.
Depending on the contents of the header, the parser may
require and end of file notification to know where the end
of the body lies. If this function returns `true` it will be
necessary to call @ref put_eof when there will never be additional
data from the input.
*/
bool
need_eof() const
{
return (f_ & flagNeedEOF) != 0;
}
/// Returns `true` if the eager parse option is set.
bool
eager() const
{
return (f_ & flagEager) != 0;
}
/** Set the eager parse option.
This option controls whether or not the parser will attempt
to consume all of the octets provided during parsing, or
if it will stop when it reaches one of the following positions
within the serialized message:
@li Immediately after the header
@li Immediately after any chunk header
The default is to stop after the header or any chunk header.
*/
void
eager(bool v)
{
if(v)
f_ |= flagEager;
else
f_ &= ~flagEager;
}
/// Returns `true` if the parser will ignore the message body.
bool
skip()
{
return (f_ & flagSkipBody) != 0;
}
/** Set the skip body option.
The option controls whether or not the parser expects to
see an HTTP body, regardless of the presence or absence of
certain fields such as Content-Length.
Depending on the request, some responses do not carry a
body. For example, a 200 response to a CONNECT request
from a tunneling proxy. In these cases, callers may use
this function inform the parser that no body is expected.
The parser will consider the message complete after the
header has been received.
@note This function must called before any bytes are processed.
*/
void
skip(bool v);
/** Write a buffer sequence to the parser.
This function attempts to incrementally parse the HTTP
message data stored in the caller provided buffers. Upon
success, a positive return value indicates that the parser
made forward progress, consuming that number of
bytes.
A return value of zero indicates that the parser
requires additional input. In this case the caller
should append additional bytes to the input buffer
sequence and call @ref write again.
In some cases there may be an insufficient number of octets
in the input buffer in order to make forward progress. This
is indicated by the the code @ref error::need_more. When
this happens, the caller should place additional bytes into
the buffer sequence and call @ref put again.
The error code @ref error::need_more is special. When this
error is returned, a subsequent call to @ref put may succeed
if the buffers have been updated. Otherwise, upon error
the parser may not be restarted.
@param buffers An object meeting the requirements of
@b ConstBufferSequence that represents the message.
@param ec Set to the error, if any occurred.
@return The number of bytes consumed in the buffer
sequence.
@return The number of octets consumed in the buffer
sequence. The caller should remove these octets even if the
error is set.
*/
template<class ConstBufferSequence>
std::size_t
write(ConstBufferSequence const& buffers, error_code& ec);
put(ConstBufferSequence const& buffers, error_code& ec);
#if ! BEAST_DOXYGEN
std::size_t
write(boost::asio::const_buffers_1 const& buffer,
put(boost::asio::const_buffers_1 const& buffer,
error_code& ec);
#endif
@ -423,139 +419,7 @@ public:
@param ec Set to the error, if any occurred.
*/
void
write_eof(error_code& ec);
/** Returns the number of bytes remaining in the body or chunk.
If a Content-Length is specified and the parser state
is equal to @ref beast::http::parse_state::body, this will return
the number of bytes remaining in the body. If the
chunked Transfer-Encoding is indicated and the parser
state is equal to @ref beast::http::parse_state::chunk_body, this
will return the number of bytes remaining in the chunk.
Otherwise, the function behavior is undefined.
*/
std::uint64_t
size() const
{
BOOST_ASSERT(
state_ == parse_state::body ||
state_ == parse_state::chunk_body);
return len_;
}
/** Returns the body data parsed in the last call to @ref write.
This buffer is invalidated after any call to @ref write
or @ref write_eof.
@note If the last call to @ref write came from the input
area of a @b DynamicBuffer object, a call to the dynamic
buffer's `consume` function may invalidate this return
value.
*/
string_view const&
body() const
{
// This function not available when isDirect==true
BOOST_STATIC_ASSERT(! isDirect);
return body_;
}
/** Returns the chunk extension parsed in the last call to @ref write.
This buffer is invalidated after any call to @ref write
or @ref write_eof.
@note If the last call to @ref write came from the input
area of a @b DynamicBuffer object, a call to the dynamic
buffer's `consume` function may invalidate this return
value.
*/
string_view const&
chunk_extension() const
{
// This function not available when isDirect==true
BOOST_STATIC_ASSERT(! isDirect);
return ext_;
}
/** Returns the optional value of Content-Length if known.
@note The return value is undefined unless a complete
header has been parsed.
*/
boost::optional<std::uint64_t>
content_length() const
{
BOOST_ASSERT(got_header());
if(! (f_ & flagContentLength))
return boost::none;
return len_;
}
/** Copy leftover body data from the dynamic buffer.
@note This member function is only available when
`isDirect==true`.
@return The number of bytes processed from the dynamic
buffer. The caller should remove these bytes by calling
`consume` on the buffer.
*/
template<class DynamicBuffer>
std::size_t
copy_body(DynamicBuffer& buffer);
/** Returns a set of buffers for storing body data.
@param buffers A writable output parameter into which
the function will place the buffers upon success.
@param limit The maximum number of bytes in the
size of the returned buffer sequence. The actual size
of the buffer sequence may be lower than this number.
@note This member function is only available when
`isDirect==true`.
*/
template<class MutableBufferSequence>
void
prepare_body(boost::optional<
MutableBufferSequence>& buffers, std::size_t limit);
/** Commit body data.
@param n The number of bytes to commit. This must
be less than or equal to the size of the buffer
sequence returned by @ref prepare_body.
@note This member function is only available when
`isDirect==true`.
*/
void
commit_body(std::size_t n);
/** Indicate that body octets have been consumed.
@param n The number of bytes to consume.
*/
void
consume(std::size_t n)
{
BOOST_ASSERT(n <= len_);
BOOST_ASSERT(
state_ == parse_state::body ||
state_ == parse_state::chunk_body);
len_ -= n;
if(len_ == 0)
{
if(state_ == parse_state::body)
state_ = parse_state::complete;
else
state_ = parse_state::chunk_header;
}
}
put_eof(error_code& ec);
private:
inline
@ -570,68 +434,42 @@ private:
maybe_flatten(
ConstBufferSequence const& buffers);
std::size_t
do_write(boost::asio::const_buffers_1 const& buffer,
void
parse_header(char const*& p,
std::size_t n, error_code& ec);
void
parse_header(char const*& p, char const* term,
error_code& ec, std::true_type);
std::size_t
do_write(boost::asio::const_buffers_1 const& buffer,
void
parse_header(char const*& p, char const* term,
error_code& ec, std::false_type);
void
parse_startline(char const*& it,
int& version, int& status,
error_code& ec, std::true_type);
parse_body(char const*& p,
std::size_t n, error_code& ec);
void
parse_startline(char const*& it,
int& version, int& status,
error_code& ec, std::false_type);
parse_body_to_eof(char const*& p,
std::size_t n, error_code& ec);
void
parse_fields(char const*& it,
parse_chunk_header(char const*& p,
std::size_t n, error_code& ec);
void
parse_chunk_body(char const*& p,
std::size_t n, error_code& ec);
void
parse_fields(char const*& p,
char const* last, error_code& ec);
void
do_field(
string_view const& name,
string_view const& value,
error_code& ec);
std::size_t
parse_header(char const* p,
std::size_t n, error_code& ec);
void
do_header(int, std::true_type);
void
do_header(int status, std::false_type);
void
maybe_do_body_direct();
void
maybe_do_body_indirect(error_code& ec);
std::size_t
parse_chunk_header(char const* p,
std::size_t n, error_code& ec);
std::size_t
parse_body(char const* p,
std::size_t n, error_code& ec);
std::size_t
parse_body_to_eof(char const* p,
std::size_t n, error_code& ec);
std::size_t
parse_chunk_body(char const* p,
std::size_t n, error_code& ec);
void
do_complete(error_code& ec);
do_field(string_view const& name,
string_view const& value,
error_code& ec);
};
} // http

View File

@ -8,9 +8,9 @@
#ifndef BEAST_HTTP_BUFFER_BODY_HPP
#define BEAST_HTTP_BUFFER_BODY_HPP
#include <beast/http/type_traits.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message.hpp>
#include <beast/http/type_traits.hpp>
#include <boost/optional.hpp>
#include <type_traits>
#include <utility>
@ -31,41 +31,30 @@ namespace http {
...
}
@endcode
@tparam isDeferred A `bool` which, when set to `true`,
indicates to the serialization implementation that it should
send the entire HTTP Header before attempting to acquire
buffers representing the body.
@tparam ConstBufferSequence The type of buffer sequence
stored in the body by the caller.
*/
template<
bool isDeferred,
class ConstBufferSequence>
struct buffer_body
{
static_assert(is_const_buffer_sequence<ConstBufferSequence>::value,
"ConstBufferSequence requirements not met");
/// The type of the body member when used in a message.
struct value_type
{
/** A pointer to a contiguous area of memory of @ref size octets, else `nullptr`.
/** The type of the body member when used in a message.
If this is `nullptr` and @ref more is `true`, the error
@ref error::need_buffer will be returned by a serializer
when attempting to retrieve the next buffer.
*/
void* data;
When engaged, the first element of the pair represents
the current buffer sequence to be written.
/** The number of octets in the buffer pointed to by @ref data
The second element of the pair indicates whether or not
additional buffers will be available. A value of `false`
indicates the end of the message body.
If @ref data is `nullptr` during serialization, this value
is not inspected.
*/
std::size_t size;
If the buffer in the value is disengaged, and the second
element of the pair is `true`, @ref serializer operations
will return @ref http::error::need_more. This signals the
calling code that a new buffer should be placed into the
body, or that the caller should indicate that no more
buffers will be available.
*/
using value_type =
std::pair<boost::optional<ConstBufferSequence>, bool>;
/// `true` if this is not the last buffer.
bool more;
};
#if BEAST_DOXYGEN
/// The algorithm to obtain buffers representing the body
@ -77,10 +66,10 @@ struct buffer_body
value_type const& body_;
public:
using is_deferred =
std::integral_constant<bool, isDeferred>;
using is_deferred = std::false_type;
using const_buffers_type = ConstBufferSequence;
using const_buffers_type =
boost::asio::const_buffers_1;
template<bool isRequest, class Fields>
explicit
@ -95,44 +84,89 @@ struct buffer_body
{
}
boost::optional<std::pair<ConstBufferSequence, bool>>
boost::optional<
std::pair<const_buffers_type, bool>>
get(error_code& ec)
{
if(toggle_)
{
if(body_.second)
if(body_.more)
{
toggle_ = false;
ec = error::need_more;
ec = error::need_buffer;
}
return boost::none;
}
if(body_.first)
if(body_.data)
{
toggle_ = true;
return {{*body_.first, body_.second}};
return {{const_buffers_type{
body_.data, body_.size}, body_.more}};
}
if(body_.second)
ec = error::need_more;
if(body_.more)
ec = error::need_buffer;
return boost::none;
}
};
#endif
#if BEAST_DOXYGEN
/// The algorithm used store buffers in this body
using writer = implementation_defined;
#else
class writer
{
value_type& body_;
public:
template<bool isRequest, class Fields>
explicit
writer(message<isRequest, buffer_body, Fields>& m)
: body_(m.body)
{
}
void
init(boost::optional<std::uint64_t>, error_code&)
{
}
template<class ConstBufferSequence>
void
put(ConstBufferSequence const& buffers,
error_code& ec)
{
using boost::asio::buffer_size;
using boost::asio::buffer_copy;
auto const n = buffer_size(buffers);
if(! body_.data || n > body_.size)
{
ec = error::need_buffer;
return;
}
auto const bytes_transferred =
buffer_copy(boost::asio::buffer(
body_.data, body_.size), buffers);
body_.data = reinterpret_cast<char*>(
body_.data) + bytes_transferred;
body_.size -= bytes_transferred;
}
void
finish(error_code&)
{
}
};
#endif
};
#if ! BEAST_DOXYGEN
// operator<< is not supported for buffer_body"
template<bool isRequest, class Fields,
bool isDeferred, class ConstBufferSequence>
std::ostream&
operator<<(std::ostream& os, message<
isRequest, buffer_body<isDeferred,
ConstBufferSequence>, Fields> const& msg)
{
static_assert(sizeof(ConstBufferSequence) == -1,
"operator<< is not supported for buffer_body");
}
operator<<(std::ostream& os, message<isRequest,
buffer_body, Fields> const& msg) = delete;
#endif
} // http

View File

@ -62,6 +62,20 @@ namespace detail {
class basic_parser_base
{
protected:
enum class state
{
nothing_yet = 0,
header,
body0,
body,
body_to_eof0,
body_to_eof,
chunk_header0,
chunk_header,
chunk_body,
complete
};
static
bool
is_pathchar(char c)
@ -263,11 +277,9 @@ protected:
bool
parse_crlf(char const*& it)
{
if(*it != '\r')
if( it[0] != '\r' && it[1] != '\n')
return false;
if(*++it != '\n')
return false;
++it;
it += 2;
return true;
}

View File

@ -9,8 +9,8 @@
#define BEAST_HTTP_DYNAMIC_BODY_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
#include <beast/core/multi_buffer.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message.hpp>
#include <boost/optional.hpp>
#include <utility>
@ -28,57 +28,6 @@ struct basic_dynamic_body
/// The type of the body member when used in a message.
using value_type = DynamicBuffer;
#if BEAST_DOXYGEN
/// The algorithm used store buffers in this body
using writer = implementation_defined;
#else
class writer
{
value_type& body_;
public:
static bool constexpr is_direct = true;
using mutable_buffers_type =
typename DynamicBuffer::mutable_buffers_type;
template<bool isRequest, class Fields>
explicit
writer(message<isRequest,
basic_dynamic_body, Fields>& msg)
: body_(msg.body)
{
}
void
init()
{
}
void
init(std::uint64_t content_length)
{
}
mutable_buffers_type
prepare(std::size_t n)
{
return body_.prepare(n);
}
void
commit(std::size_t n)
{
body_.commit(n);
}
void
finish()
{
}
};
#endif
#if BEAST_DOXYGEN
/// The algorithm to obtain buffers representing the body
using reader = implementation_defined;
@ -119,6 +68,58 @@ struct basic_dynamic_body
}
};
#endif
#if BEAST_DOXYGEN
/// The algorithm used store buffers in this body
using writer = implementation_defined;
#else
class writer
{
value_type& body_;
public:
template<bool isRequest, class Fields>
explicit
writer(message<isRequest,
basic_dynamic_body, Fields>& msg)
: body_(msg.body)
{
}
void
init(boost::optional<
std::uint64_t> const&, error_code&)
{
}
template<class ConstBufferSequence>
void
put(ConstBufferSequence const& buffers,
error_code& ec)
{
using boost::asio::buffer_copy;
using boost::asio::buffer_size;
boost::optional<typename
DynamicBuffer::mutable_buffers_type> b;
try
{
b.emplace(body_.prepare(
buffer_size(buffers)));
}
catch(std::length_error const&)
{
ec = error::buffer_overflow;
return;
}
body_.commit(buffer_copy(*b, buffers));
}
void
finish(error_code&)
{
}
};
#endif
};
/** A dynamic message body represented by a @ref multi_buffer

View File

@ -9,6 +9,7 @@
#define BEAST_HTTP_EMPTY_BODY_HPP
#include <beast/config.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message.hpp>
#include <boost/optional.hpp>
@ -26,6 +27,8 @@ struct empty_body
/// The type of the body member when used in a message.
struct value_type
{
// VFALCO We could stash boost::optional<std::uint64_t>
// for the content length here, set on init()
};
#if BEAST_DOXYGEN
@ -64,6 +67,39 @@ struct empty_body
}
};
#endif
#if BEAST_DOXYGEN
/// The algorithm used store buffers in this body
using writer = implementation_defined;
#else
struct writer
{
template<bool isRequest, class Fields>
explicit
writer(message<isRequest, empty_body, Fields>& msg)
{
}
void
init(boost::optional<std::uint64_t> const&,
error_code&)
{
}
template<class ConstBufferSequence>
void
put(ConstBufferSequence const&,
error_code& ec)
{
ec = error::missing_body;
}
void
finish(error_code&)
{
}
};
#endif
};
} // http

View File

@ -14,36 +14,64 @@
namespace beast {
namespace http {
/// Error codes returned from HTTP parsing
/// Error codes returned from HTTP algorithms and operations.
enum class error
{
/** The end of the stream was reached.
This error is returned by @ref basic_parser::write_eof
when the end of stream is reached and there are no
unparsed bytes in the stream buffer.
This error is returned under the following conditions:
@li When attempting to read HTTP data from a stream and the stream
read returns the error `boost::asio::error::eof` before any new octets
have been received.
@li When sending a complete HTTP message at once and the semantics of
the message are that the connection should be closed to indicate the
end of the message.
*/
end_of_stream = 1,
/** The incoming message is incomplete.
This happens when the end of stream is reached
and some bytes have been received, but not the
This happens when the end of stream is reached during
parsing and some octets have been received, but not the
entire message.
*/
partial_message,
/** Additional buffers are required.
This error is generated during serialization of HTTP
messages using the @ref buffer_body representation.
It indicates to the caller that an additional buffer
sequence should be placed into the body, or that the
caller should indicate that there are no more bytes
remaining in the body to serialize.
This error is returned during parsing when additional
octets are needed. The caller should append more data
to the existing buffer and retry the parse operaetion.
*/
need_more,
/** The message container has no body.
This error is returned when attempting to parse body
octets into a message container which has the
@ref empty_body body type.
@see @ref empty_body
*/
missing_body,
/** Additional buffers are required.
This error is returned under the following conditions:
@li During serialization when using @ref buffer_body.
The caller should update the body to point to a new
buffer or indicate that there are no more octets in
the body.
@li During parsing when using @ref buffer_body.
The caller should update the body to point to a new
storage area to receive additional body octets.
*/
need_buffer,
/** Buffer maximum exceeded.
This error is returned when reading HTTP content
@ -52,6 +80,10 @@ enum class error
*/
buffer_overflow,
//
// (parser errors)
//
/// The line ending was malformed
bad_line_ending,

View File

@ -33,18 +33,19 @@ namespace http {
*/
template<bool isRequest, class Fields>
class header_parser
: public basic_parser<isRequest, false,
: public basic_parser<isRequest,
header_parser<isRequest, Fields>>
{
header<isRequest, Fields> h_;
string_view body_;
public:
using mutable_buffers_type =
boost::asio::null_buffers;
/// The type of @ref header this object produces.
using value_type = header<isRequest, Fields>;
/// Default constructor.
header_parser() = default;
/// Copy constructor.
header_parser(header_parser const&) = default;
@ -58,18 +59,49 @@ public:
*/
header_parser(header_parser&&) = default;
/** Move assignment
After the move, the only valid operation
on the moved-from object is destruction.
*/
header_parser& operator=(header_parser&&) = default;
/** Constructor
@param args If present, additional arguments to be
forwarded to the @ref beast::http::header constructor.
@param args Optional arguments forwarded
forwarded to the @ref http::header constructor.
*/
#if BEAST_DOXYGEN
template<class... Args>
explicit
header_parser(Args&&... args);
#else
template<class Arg0, class... ArgN,
class = typename std::enable_if<
! std::is_convertible<typename
std::decay<Arg0>::type,
header_parser>::value>>
explicit
header_parser(Arg0&& arg0, ArgN&&... argn);
#endif
/** Returns parsed body octets.
This function will return the most recent buffer
of octets corresponding to the parsed body. This
buffer will become invalidated on any subsequent
call to @ref put or @ref put_eof
*/
string_view
body() const
{
return body_;
}
/** Returns the parsed header
Only valid if @ref got_header would return `true`.
@note The return value is undefined unless
@ref is_header_done would return `true`.
*/
value_type const&
get() const
@ -79,7 +111,8 @@ public:
/** Returns the parsed header.
Only valid if @ref got_header would return `true`.
@note The return value is undefined unless
@ref is_header_done would return `true`.
*/
value_type&
get()
@ -89,8 +122,10 @@ public:
/** Returns ownership of the parsed header.
Ownership is transferred to the caller. Only
valid if @ref got_header would return `true`.
Ownership is transferred to the caller.
@note The return value is undefined unless
@ref is_header_done would return `true`.
Requires:
@ref value_type is @b MoveConstructible
@ -98,20 +133,18 @@ public:
value_type
release()
{
static_assert(std::is_move_constructible<decltype(h_)>::value,
static_assert(
std::is_move_constructible<decltype(h_)>::value,
"MoveConstructible requirements not met");
return std::move(h_);
}
private:
friend class basic_parser<
isRequest, false, header_parser>;
friend class basic_parser<isRequest, header_parser>;
void
on_request(
string_view const& method,
string_view const& path,
int version, error_code&)
on_request(string_view method,
string_view path, int version, error_code&)
{
h_.target(path);
h_.method(method);
@ -119,9 +152,8 @@ private:
}
void
on_response(int status,
string_view const& reason,
int version, error_code&)
on_response(int status, string_view reason,
int version, error_code&)
{
h_.status = status;
h_.version = version;
@ -129,9 +161,8 @@ private:
}
void
on_field(string_view const& name,
string_view const& value,
error_code&)
on_field(string_view name,
string_view value, error_code&)
{
h_.fields.insert(name, value);
}
@ -142,41 +173,28 @@ private:
}
void
on_body(error_code& ec)
on_body(boost::optional<std::
uint64_t> const&, error_code&)
{
}
void
on_body(std::uint64_t content_length,
error_code& ec)
on_data(string_view s, error_code&)
{
body_ = s;
}
void
on_data(string_view const& s,
error_code& ec)
{
}
void
on_commit(std::size_t n)
{
// Can't write body data with header-only parser!
BOOST_ASSERT(false);
BOOST_THROW_EXCEPTION(std::logic_error{
"invalid member function call"});
}
void
on_chunk(std::uint64_t n,
string_view const& ext,
error_code& ec)
on_chunk(std::uint64_t,
string_view const&, error_code&)
{
body_ = {};
}
void
on_complete(error_code&)
{
body_ = {};
}
};

View File

@ -1,733 +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_ASYNC_READ_IPP_HPP
#define BEAST_HTTP_IMPL_ASYNC_READ_IPP_HPP
#include <beast/http/type_traits.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_ptr.hpp>
#include <beast/core/type_traits.hpp>
#include <beast/core/detail/read_size_helper.hpp>
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.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_)
: s(s_)
, db(db_)
, p(p_)
{
using boost::asio::asio_handler_is_continuation;
cont = asio_handler_is_continuation(std::addressof(handler));
}
};
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)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->d_.handler()));
}
friend
void
asio_handler_deallocate(
void* p, std::size_t size,
read_some_buffer_op* op)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(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)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(
f, std::addressof(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:
{
using beast::detail::read_size_helper;
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())
{
ec = error::end_of_stream;
goto upcall;
}
// caller sees end_of_stream on next read.
ec = {};
d.p.write_eof(ec);
if(ec)
goto upcall;
BOOST_ASSERT(d.p.is_complete());
goto upcall;
}
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_)
: s(s_)
, db(db_)
, p(p_)
{
using boost::asio::asio_handler_is_continuation;
cont = asio_handler_is_continuation(std::addressof(handler));
}
};
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)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->d_.handler()));
}
friend
void
asio_handler_deallocate(
void* p, std::size_t size,
read_some_body_op* op)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(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)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(
f, std::addressof(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_)
: s(s_)
, db(db_)
, p(p_)
{
using boost::asio::asio_handler_is_continuation;
cont = asio_handler_is_continuation(std::addressof(handler));
}
};
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)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->d_.handler()));
}
friend
void
asio_handler_deallocate(
void* p, std::size_t size,
parse_op* op)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(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)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(
f, std::addressof(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_)
: s(s_)
, db(sb_)
, m(m_)
{
using boost::asio::asio_handler_is_continuation;
cont = asio_handler_is_continuation(std::addressof(handler));
}
};
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)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->d_.handler()));
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, read_message_op* op)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(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)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(
f, std::addressof(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>
async_return_type<
ReadHandler, void(error_code, std::size_t)>
async_read_some(
AsyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, true, Derived>& parser,
ReadHandler&& handler)
{
async_completion<ReadHandler,
void(error_code, std::size_t)> init{handler};
switch(parser.state())
{
case parse_state::header:
case parse_state::chunk_header:
detail::read_some_buffer_op<AsyncReadStream,
DynamicBuffer, isRequest, true, Derived, handler_type<
ReadHandler, void(error_code, std::size_t)>>{
init.completion_handler, stream, buffer, parser};
break;
default:
detail::read_some_body_op<AsyncReadStream,
DynamicBuffer, isRequest, Derived, handler_type<
ReadHandler, void(error_code, std::size_t)>>{
init.completion_handler, stream, buffer, parser};
break;
}
return init.result.get();
}
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived,
class ReadHandler>
inline
async_return_type<
ReadHandler, void(error_code, std::size_t)>
async_read_some(
AsyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, false, Derived>& parser,
ReadHandler&& handler)
{
async_completion<ReadHandler,
void(error_code, std::size_t)> init{handler};
detail::read_some_buffer_op<AsyncReadStream,
DynamicBuffer, isRequest, false, Derived, handler_type<
ReadHandler, void(error_code, std::size_t)>>{
init.completion_handler, stream, buffer, parser};
return init.result.get();
}
} // detail
//------------------------------------------------------------------------------
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived,
class ReadHandler>
async_return_type<
ReadHandler, void(error_code, std::size_t)>
async_read_some(
AsyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_complete());
return detail::async_read_some(stream, buffer, parser,
std::forward<ReadHandler>(handler));
}
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived,
class ReadHandler>
async_return_type<
ReadHandler, void(error_code)>
async_read(
AsyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_complete());
async_completion<ReadHandler,
void(error_code)> init{handler};
detail::parse_op<AsyncReadStream, DynamicBuffer,
isRequest, isDirect, Derived, handler_type<
ReadHandler, void(error_code)>>{
init.completion_handler, stream, buffer, parser};
return init.result.get();
}
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, class Body, class Fields,
class ReadHandler>
async_return_type<
ReadHandler, void(error_code)>
async_read(
AsyncReadStream& stream,
DynamicBuffer& buffer,
message<isRequest, Body, Fields>& msg,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_body<Body>::value,
"Body requirements not met");
static_assert(is_body_writer<Body>::value,
"BodyWriter requirements not met");
async_completion<ReadHandler,
void(error_code)> init{handler};
detail::read_message_op<AsyncReadStream, DynamicBuffer,
isRequest, Body, Fields, handler_type<
ReadHandler, void(error_code)>>{
init.completion_handler, stream, buffer, msg};
return init.result.get();
}
} // http
} // beast
#endif

File diff suppressed because it is too large Load Diff

View File

@ -12,10 +12,11 @@ namespace beast {
namespace http {
template<bool isRequest, class Fields>
template<class... Args>
template<class Arg0, class... ArgN, class>
header_parser<isRequest, Fields>::
header_parser(Args&&... args)
: h_(std::forward<Args>(args)...)
header_parser(Arg0&& arg0, ArgN&&... argn)
: h_(std::forward<Arg0>(arg0),
std::forward<ArgN>(argn)...)
{
}

View File

@ -26,8 +26,7 @@ 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)))
isRequest, header_parser<isRequest, Fields>>&>(parser)))
, m_(parser.release(), std::forward<Args>(args)...)
{
}

View File

@ -28,159 +28,396 @@ namespace http {
namespace detail {
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
inline
std::size_t
read_some_buffer(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser,
error_code& ec)
//------------------------------------------------------------------------------
template<class Stream, class DynamicBuffer,
bool isRequest, class Derived, class Handler>
class read_some_op
{
std::size_t bytes_used;
if(buffer.size() == 0)
goto do_read;
for(;;)
int state_ = 0;
std::size_t used_ = 0;
Stream& s_;
DynamicBuffer& b_;
basic_parser<isRequest, Derived>& p_;
boost::optional<typename
DynamicBuffer::mutable_buffers_type> mb_;
Handler h_;
public:
read_some_op(read_some_op&&) = default;
read_some_op(read_some_op const&) = default;
template<class DeducedHandler>
read_some_op(DeducedHandler&& h, Stream& s,
DynamicBuffer& b, basic_parser<isRequest, Derived>& p)
: s_(s)
, b_(b)
, p_(p)
, h_(std::forward<DeducedHandler>(h))
{
bytes_used = parser.write(
buffer.data(), ec);
}
void
operator()(error_code ec, std::size_t bytes_transferred);
friend
void* asio_handler_allocate(
std::size_t size, read_some_op* op)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->h_));
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, read_some_op* op)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(op->h_));
}
friend
bool asio_handler_is_continuation(read_some_op* op)
{
using boost::asio::asio_handler_is_continuation;
return op->state_ >= 2 ? true :
asio_handler_is_continuation(
std::addressof(op->h_));
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, read_some_op* op)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(
f, std::addressof(op->h_));
}
};
template<class Stream, class DynamicBuffer,
bool isRequest, class Derived, class Handler>
void
read_some_op<Stream, DynamicBuffer,
isRequest, Derived, Handler>::
operator()(error_code ec, std::size_t bytes_transferred)
{
switch(state_)
{
case 0:
state_ = 1;
if(b_.size() == 0)
goto do_read;
goto do_parse;
case 1:
state_ = 2;
case 2:
if(ec == boost::asio::error::eof)
{
BOOST_ASSERT(bytes_transferred == 0);
if(p_.got_some())
{
// caller sees EOF on next read
ec = {};
p_.put_eof(ec);
if(ec)
goto upcall;
BOOST_ASSERT(p_.is_done());
goto upcall;
}
ec = error::end_of_stream;
goto upcall;
}
if(ec)
return 0;
if(bytes_used > 0)
goto do_finish;
goto upcall;
b_.commit(bytes_transferred);
do_parse:
used_ += p_.put(b_.data(), ec);
if(! ec || ec != http::error::need_more)
goto do_upcall;
ec = {};
// [[fallthrough]]
do_read:
boost::optional<typename
DynamicBuffer::mutable_buffers_type> mb;
using beast::detail::read_size_helper;
auto const size =
read_size_helper(buffer, 65536);
BOOST_ASSERT(size > 0);
try
{
mb.emplace(buffer.prepare(size));
using beast::detail::read_size_helper;
mb_.emplace(b_.prepare(
read_size_helper(b_, 65536)));
}
catch(std::length_error const&)
{
ec = error::buffer_overflow;
return 0;
goto do_upcall;
}
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;
}
ec = error::end_of_stream;
break;
}
else if(ec)
{
return 0;
}
BOOST_ASSERT(bytes_transferred > 0);
buffer.commit(bytes_transferred);
return s_.async_read_some(*mb_, std::move(*this));
do_upcall:
if(state_ >= 2)
goto upcall;
state_ = 3;
return s_.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
case 3:
break;
}
do_finish:
return bytes_used;
upcall:
h_(ec, used_);
}
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived>
inline
std::size_t
read_some_body(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, true, Derived>& parser,
error_code& ec)
//------------------------------------------------------------------------------
struct parser_is_done
{
if(buffer.size() > 0)
return parser.copy_body(buffer);
boost::optional<typename
Derived::mutable_buffers_type> mb;
try
template<bool isRequest, class Derived>
bool
operator()(basic_parser<
isRequest, Derived> const& p) const
{
parser.prepare_body(mb, 65536);
return p.is_done();
}
catch(std::length_error const&)
};
struct parser_is_header_done
{
template<bool isRequest, class Derived>
bool
operator()(basic_parser<
isRequest, Derived> const& p) const
{
ec = error::buffer_overflow;
return 0;
return p.is_header_done();
}
auto const bytes_transferred =
stream.read_some(*mb, ec);
if(ec == boost::asio::error::eof)
};
template<class Stream, class DynamicBuffer,
bool isRequest, class Derived, class Condition,
class Handler>
class read_op
{
int state_ = 0;
Stream& s_;
DynamicBuffer& b_;
basic_parser<isRequest, Derived>& p_;
Handler h_;
public:
read_op(read_op&&) = default;
read_op(read_op const&) = default;
template<class DeducedHandler>
read_op(DeducedHandler&& h, Stream& s,
DynamicBuffer& b, basic_parser<isRequest,
Derived>& p)
: s_(s)
, b_(b)
, p_(p)
, h_(std::forward<DeducedHandler>(h))
{
BOOST_ASSERT(bytes_transferred == 0);
// caller sees EOF on next read
ec = {};
parser.write_eof(ec);
}
void
operator()(error_code ec, std::size_t bytes_used);
friend
void* asio_handler_allocate(
std::size_t size, read_op* op)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->h_));
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, read_op* op)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(op->h_));
}
friend
bool asio_handler_is_continuation(read_op* op)
{
using boost::asio::asio_handler_is_continuation;
return op->state_ >= 3 ? true :
asio_handler_is_continuation(
std::addressof(op->h_));
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, read_op* op)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(
f, std::addressof(op->h_));
}
};
template<class Stream, class DynamicBuffer,
bool isRequest, class Derived, class Condition,
class Handler>
void
read_op<Stream, DynamicBuffer,
isRequest, Derived, Condition, Handler>::
operator()(error_code ec, std::size_t bytes_used)
{
switch(state_)
{
case 0:
if(Condition{}(p_))
{
state_ = 1;
return s_.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
}
state_ = 2;
// [[fallthrough]]
do_read:
return async_read_some(
s_, b_, p_, std::move(*this));
case 1:
goto upcall;
case 2:
case 3:
b_.consume(bytes_used);
if(ec)
return 0;
BOOST_ASSERT(parser.is_complete());
goto upcall;
if(Condition{}(p_))
goto upcall;
state_ = 3;
goto do_read;
}
else if(! ec)
{
parser.commit_body(bytes_transferred);
return 0;
}
return 0;
upcall:
h_(ec);
}
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived>
inline
std::size_t
read_some(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, true, Derived>& parser,
error_code& ec)
//------------------------------------------------------------------------------
template<class Stream, class DynamicBuffer,
bool isRequest, class Body, class Fields,
class Handler>
class read_msg_op
{
switch(parser.state())
using parser_type =
message_parser<isRequest, Body, Fields>;
using message_type =
message<isRequest, Body, Fields>;
struct data
{
case parse_state::header:
case parse_state::chunk_header:
return detail::read_some_buffer(
stream, buffer, parser, ec);
int state = 0;
Stream& s;
DynamicBuffer& b;
message_type& m;
parser_type p;
default:
return detail::read_some_body(
stream, buffer, parser, ec);
data(Handler& handler, Stream& s_,
DynamicBuffer& b_, message_type& m_)
: s(s_)
, b(b_)
, m(m_)
{
p.eager(true);
}
};
handler_ptr<data, Handler> d_;
public:
read_msg_op(read_msg_op&&) = default;
read_msg_op(read_msg_op const&) = default;
template<class DeducedHandler, class... Args>
read_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
s, std::forward<Args>(args)...)
{
}
}
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived>
inline
std::size_t
read_some(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, false, Derived>& parser,
error_code& ec)
void
operator()(error_code ec, std::size_t bytes_used);
friend
void* asio_handler_allocate(
std::size_t size, read_msg_op* op)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->d_.handler()));
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, read_msg_op* op)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(op->d_.handler()));
}
friend
bool asio_handler_is_continuation(read_msg_op* op)
{
using boost::asio::asio_handler_is_continuation;
return op->d_->state >= 2 ? true :
asio_handler_is_continuation(
std::addressof(op->d_.handler()));
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, read_msg_op* op)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(
f, std::addressof(op->d_.handler()));
}
};
template<class Stream, class DynamicBuffer,
bool isRequest, class Body, class Fields,
class Handler>
void
read_msg_op<Stream, DynamicBuffer,
isRequest, Body, Fields, Handler>::
operator()(error_code ec, std::size_t bytes_used)
{
return detail::read_some_buffer(
stream, buffer, parser, ec);
auto& d = *d_;
switch(d.state)
{
case 0:
d.state = 1;
do_read:
return async_read_some(
d.s, d.b, d.p, std::move(*this));
case 1:
case 2:
d.b.consume(bytes_used);
if(ec)
goto upcall;
if(d.p.is_done())
{
d.m = d.p.release();
goto upcall;
}
d.state = 2;
goto do_read;
}
upcall:
d_.invoke(ec);
}
} // detail
@ -190,18 +427,18 @@ read_some(
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
bool isRequest, class Derived>
std::size_t
read_some(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser)
basic_parser<isRequest, Derived>& parser)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_complete());
BOOST_ASSERT(! parser.is_done());
error_code ec;
auto const bytes_used =
read_some(stream, buffer, parser, ec);
@ -213,37 +450,190 @@ read_some(
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
bool isRequest, class Derived>
std::size_t
read_some(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_complete());
return detail::read_some(stream, buffer, parser, ec);
BOOST_ASSERT(! parser.is_done());
std::size_t bytes_used = 0;
if(buffer.size() == 0)
goto do_read;
for(;;)
{
// invoke parser
bytes_used += parser.put(buffer.data(), ec);
if(! ec)
break;
if(ec != http::error::need_more)
break;
ec = {};
do_read:
boost::optional<typename
DynamicBuffer::mutable_buffers_type> b;
try
{
using beast::detail::read_size_helper;
b.emplace(buffer.prepare(
read_size_helper(buffer, 65536)));
}
catch(std::length_error const&)
{
ec = error::buffer_overflow;
break;
}
auto const bytes_transferred =
stream.read_some(*b, ec);
if(ec == boost::asio::error::eof)
{
BOOST_ASSERT(bytes_transferred == 0);
if(parser.got_some())
{
// caller sees EOF on next read
ec = {};
parser.put_eof(ec);
if(ec)
break;
BOOST_ASSERT(parser.is_done());
break;
}
ec = error::end_of_stream;
break;
}
if(ec)
break;
buffer.commit(bytes_transferred);
}
return bytes_used;
}
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived,
class ReadHandler>
async_return_type<
ReadHandler, void(error_code, std::size_t)>
async_read_some(
AsyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_done());
async_completion<ReadHandler,
void(error_code, std::size_t)> init{handler};
detail::read_some_op<AsyncReadStream,
DynamicBuffer, isRequest, Derived, handler_type<
ReadHandler, void(error_code, std::size_t)>>{
init.completion_handler, stream, buffer, parser}(
error_code{}, 0);
return init.result.get();
}
//------------------------------------------------------------------------------
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
bool isRequest, class Derived>
void
read(
read_header(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser)
basic_parser<isRequest, Derived>& parser)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
error_code ec;
read_header(stream, buffer, parser, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
}
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived>
void
read_header(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
parser.eager(false);
while(! parser.is_header_done())
{
auto const bytes_used =
read_some(stream, buffer, parser, ec);
buffer.consume(bytes_used);
if(ec)
return;
}
}
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived,
class ReadHandler>
async_return_type<
ReadHandler, void(error_code)>
async_read_header(
AsyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
parser.eager(false);
async_completion<ReadHandler,
void(error_code)> init{handler};
detail::read_op<AsyncReadStream, DynamicBuffer,
isRequest, Derived, detail::parser_is_header_done,
handler_type<ReadHandler, void(error_code)>>{
init.completion_handler, stream, buffer, parser}(
error_code{}, 0);
return init.result.get();
}
//------------------------------------------------------------------------------
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived>
void
read(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_complete());
error_code ec;
read(stream, buffer, parser, ec);
if(ec)
@ -253,30 +643,59 @@ read(
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
bool isRequest, class Derived>
void
read(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
BOOST_ASSERT(! parser.is_complete());
do
parser.eager(true);
while(! parser.is_done())
{
auto const bytes_used =
read_some(stream, buffer, parser, ec);
auto const bytes_used = read_some(
stream, buffer, parser, ec);
buffer.consume(bytes_used);
if(ec)
return;
buffer.consume(bytes_used);
}
while(! parser.is_complete());
}
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived,
class ReadHandler>
async_return_type<
ReadHandler, void(error_code)>
async_read(
AsyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
parser.eager(true);
async_completion<ReadHandler,
void(error_code)> init{handler};
detail::read_op<AsyncReadStream, DynamicBuffer,
isRequest, Derived, detail::parser_is_done,
handler_type<ReadHandler, void(error_code)>>{
init.completion_handler, stream, buffer, parser}(
error_code{}, 0);
return init.result.get();
}
//------------------------------------------------------------------------------
template<
class SyncReadStream,
class DynamicBuffer,
@ -296,7 +715,7 @@ read(
static_assert(is_body_writer<Body>::value,
"BodyWriter requirements not met");
error_code ec;
beast::http::read(stream, buffer, msg, ec);
read(stream, buffer, msg, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
}
@ -321,12 +740,44 @@ read(
static_assert(is_body_writer<Body>::value,
"BodyWriter requirements not met");
message_parser<isRequest, Body, Fields> p;
beast::http::read(stream, buffer, p, ec);
p.eager(true);
read(stream, buffer, p.base(), ec);
if(ec)
return;
msg = p.release();
}
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, class Body, class Fields,
class ReadHandler>
async_return_type<
ReadHandler, void(error_code)>
async_read(
AsyncReadStream& stream,
DynamicBuffer& buffer,
message<isRequest, Body, Fields>& msg,
ReadHandler&& handler)
{
static_assert(is_async_read_stream<AsyncReadStream>::value,
"AsyncReadStream requirements not met");
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
"DynamicBuffer requirements not met");
static_assert(is_body<Body>::value,
"Body requirements not met");
static_assert(is_body_writer<Body>::value,
"BodyWriter requirements not met");
async_completion<ReadHandler,
void(error_code)> init{handler};
detail::read_msg_op<AsyncReadStream, DynamicBuffer,
isRequest, Body, Fields, handler_type<
ReadHandler, void(error_code)>>{
init.completion_handler, stream, buffer, msg}(
error_code{}, 0);
return init.result.get();
}
} // http
} // beast

View File

@ -81,19 +81,7 @@ serializer(message<isRequest, Body, Fields> const& m,
: m_(m)
, d_(d)
, b_(1024, alloc)
, chunked_(token_list{
m.fields["Transfer-Encoding"]}.exists("chunked"))
, close_(token_list{
m.fields["Connection"]}.exists("close") ||
(m.version < 11 && ! m.fields.exists(
"Content-Length")))
{
s_ = chunked_ ? do_init_c : do_init;
// VFALCO Move this stuff to the switch?
auto os = ostream(b_);
detail::write_start_line(os, m_);
detail::write_fields(os, m_.fields);
os << "\r\n";
}
template<bool isRequest, class Body, class Fields,
@ -107,6 +95,22 @@ get(error_code& ec, Visit&& visit)
using boost::asio::buffer_size;
switch(s_)
{
case do_construct:
{
chunked_ = token_list{
m_.fields["Transfer-Encoding"]}.exists("chunked");
close_ = token_list{m_.fields["Connection"]}.exists("close") ||
(m_.version < 11 && ! m_.fields.exists("Content-Length"));
auto os = ostream(b_);
detail::write_start_line(os, m_);
detail::write_fields(os, m_.fields);
os << "\r\n";
if(chunked_)
goto go_init_c;
s_ = do_init;
// [[fallthrough]]
}
case do_init:
{
if(split_)
@ -171,6 +175,8 @@ get(error_code& ec, Visit&& visit)
//----------------------------------------------------------------------
go_init_c:
s_ = do_init_c;
case do_init_c:
{
if(split_)
@ -320,7 +326,7 @@ consume(std::size_t n)
break;
// VFALCO delete b_?
header_done_ = true;
if(! is_deferred::value)
if(! split_)
goto go_complete;
s_ = do_body;
break;
@ -364,7 +370,7 @@ consume(std::size_t n)
break;
// VFALCO delete b_?
header_done_ = true;
if(! is_deferred::value)
if(! split_)
{
s_ = do_final_c;
break;

View File

@ -18,6 +18,7 @@
#include <boost/asio/handler_alloc_hook.hpp>
#include <boost/asio/handler_continuation_hook.hpp>
#include <boost/asio/handler_invoke_hook.hpp>
#include <boost/asio/write.hpp>
#include <boost/optional.hpp>
#include <boost/throw_exception.hpp>
#include <ostream>
@ -27,12 +28,14 @@ namespace beast {
namespace http {
namespace detail {
template<
class Stream, class Serializer, class Handler>
template<class Stream, bool isRequest, class Body,
class Fields, class Decorator, class Allocator,
class Handler>
class write_some_op
{
Stream& s_;
Serializer& sr_;
serializer<isRequest, Body, Fields,
Decorator, Allocator>& sr_;
Handler h_;
class lambda
@ -40,7 +43,7 @@ class write_some_op
write_some_op& op_;
public:
bool empty = true;
bool invoked = false;
explicit
lambda(write_some_op& op)
@ -53,7 +56,7 @@ class write_some_op
operator()(error_code& ec,
ConstBufferSequence const& buffer)
{
empty = false;
invoked = true;
return op_.s_.async_write_some(
buffer, std::move(op_));
}
@ -65,7 +68,8 @@ public:
template<class DeducedHandler>
write_some_op(DeducedHandler&& h,
Stream& s, Serializer& sr)
Stream& s, serializer<isRequest, Body,
Fields, Decorator, Allocator>& sr)
: s_(s)
, sr_(sr)
, h_(std::forward<DeducedHandler>(h))
@ -114,25 +118,40 @@ public:
}
};
template<
class Stream, class Serializer, class Handler>
template<class Stream, bool isRequest, class Body,
class Fields, class Decorator, class Allocator,
class Handler>
void
write_some_op<Stream, Serializer, Handler>::
write_some_op<Stream, isRequest, Body,
Fields, Decorator, Allocator, Handler>::
operator()()
{
lambda f{*this};
error_code ec;
if(sr_.is_done())
return s_.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
lambda f{*this};
sr_.get(ec, f);
if(! ec && ! f.empty)
if(ec)
{
BOOST_ASSERT(! f.invoked);
return s_.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
}
if(f.invoked)
// *this has been moved from,
// cannot access members here.
return;
return s_.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
}
template<
class Stream, class Serializer, class Handler>
template<class Stream, bool isRequest, class Body,
class Fields, class Decorator, class Allocator,
class Handler>
void
write_some_op<Stream, Serializer, Handler>::
write_some_op<Stream, isRequest, Body,
Fields, Decorator, Allocator, Handler>::
operator()(error_code ec, std::size_t bytes_transferred)
{
if(! ec)
@ -142,13 +161,199 @@ operator()(error_code ec, std::size_t bytes_transferred)
//------------------------------------------------------------------------------
struct serializer_is_header_done
{
template<bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
bool
operator()(serializer<isRequest, Body, Fields,
Decorator, Allocator> const& sr) const
{
return sr.is_header_done();
}
};
struct serializer_is_done
{
template<bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
bool
operator()(serializer<isRequest, Body, Fields,
Decorator, Allocator> const& sr) const
{
return sr.is_done();
}
};
template<class Stream, bool isRequest, class Body,
class Fields, class Decorator, class Allocator,
class Predicate, class Handler>
class write_op
{
int state_ = 0;
Stream& s_;
serializer<isRequest, Body, Fields,
Decorator, Allocator>& sr_;
Handler h_;
class lambda
{
write_op& op_;
public:
bool invoked = false;
explicit
lambda(write_op& op)
: op_(op)
{
}
template<class ConstBufferSequence>
void
operator()(error_code& ec,
ConstBufferSequence const& buffer)
{
invoked = true;
return op_.s_.async_write_some(
buffer, std::move(op_));
}
};
public:
write_op(write_op&&) = default;
write_op(write_op const&) = default;
template<class DeducedHandler>
write_op(DeducedHandler&& h, Stream& s,
serializer<isRequest, Body, Fields,
Decorator, Allocator>& sr)
: s_(s)
, sr_(sr)
, h_(std::forward<DeducedHandler>(h))
{
}
void
operator()(error_code ec,
std::size_t bytes_transferred);
friend
void* asio_handler_allocate(
std::size_t size, write_op* op)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
size, std::addressof(op->h_));
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, write_op* op)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
p, size, std::addressof(op->h_));
}
friend
bool asio_handler_is_continuation(write_op* op)
{
using boost::asio::asio_handler_is_continuation;
return op->state_ >= 3 ||
asio_handler_is_continuation(
std::addressof(op->h_));
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, write_op* op)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(
f, std::addressof(op->h_));
}
};
template<class Stream, bool isRequest, class Body,
class Fields, class Decorator, class Allocator,
class Predicate, class Handler>
void
write_op<Stream, isRequest, Body, Fields,
Decorator, Allocator, Predicate, Handler>::
operator()(error_code ec,
std::size_t bytes_transferred)
{
if(ec)
goto upcall;
switch(state_)
{
case 0:
{
if(Predicate{}(sr_))
{
state_ = 1;
return s_.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
}
lambda f{*this};
state_ = 2;
sr_.get(ec, f);
if(ec)
{
BOOST_ASSERT(! f.invoked);
return s_.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
}
if(f.invoked)
// *this has been moved from,
// cannot access members here.
return;
BOOST_ASSERT(Predicate{}(sr_));
state_ = 1;
return s_.get_io_service().post(
bind_handler(std::move(*this), ec, 0));
}
case 1:
goto upcall;
case 2:
state_ = 3;
// [[fallthrough]]
case 3:
{
sr_.consume(bytes_transferred);
if(Predicate{}(sr_))
goto upcall;
lambda f{*this};
sr_.get(ec, f);
if(ec)
{
BOOST_ASSERT(! f.invoked);
goto upcall;
}
if(f.invoked)
// *this has been moved from,
// cannot access members here.
return;
BOOST_ASSERT(Predicate{}(sr_));
goto upcall;
}
}
upcall:
h_(ec);
}
//------------------------------------------------------------------------------
template<class Stream, class Handler,
bool isRequest, class Body, class Fields>
class write_op
class write_msg_op
{
struct data
{
int state = 0;
Stream& s;
serializer<isRequest, Body, Fields,
empty_decorator, handler_alloc<char, Handler>> sr;
@ -165,22 +370,25 @@ class write_op
handler_ptr<data, Handler> d_;
public:
write_op(write_op&&) = default;
write_op(write_op const&) = default;
write_msg_op(write_msg_op&&) = default;
write_msg_op(write_msg_op const&) = default;
template<class DeducedHandler, class... Args>
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
write_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
: d_(std::forward<DeducedHandler>(h),
s, std::forward<Args>(args)...)
{
}
void
operator()();
void
operator()(error_code ec);
friend
void* asio_handler_allocate(
std::size_t size, write_op* op)
std::size_t size, write_msg_op* op)
{
using boost::asio::asio_handler_allocate;
return asio_handler_allocate(
@ -189,7 +397,7 @@ public:
friend
void asio_handler_deallocate(
void* p, std::size_t size, write_op* op)
void* p, std::size_t size, write_msg_op* op)
{
using boost::asio::asio_handler_deallocate;
asio_handler_deallocate(
@ -197,17 +405,16 @@ public:
}
friend
bool asio_handler_is_continuation(write_op* op)
bool asio_handler_is_continuation(write_msg_op* op)
{
using boost::asio::asio_handler_is_continuation;
return op->d_->state == 2 ||
asio_handler_is_continuation(
std::addressof(op->d_.handler()));
return asio_handler_is_continuation(
std::addressof(op->d_.handler()));
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, write_op* op)
void asio_handler_invoke(Function&& f, write_msg_op* op)
{
using boost::asio::asio_handler_invoke;
asio_handler_invoke(
@ -218,35 +425,23 @@ public:
template<class Stream, class Handler,
bool isRequest, class Body, class Fields>
void
write_op<Stream, Handler, isRequest, Body, Fields>::
write_msg_op<Stream, Handler, isRequest, Body, Fields>::
operator()()
{
auto& d = *d_;
return async_write(d.s, d.sr, std::move(*this));
}
template<class Stream, class Handler,
bool isRequest, class Body, class Fields>
void
write_msg_op<Stream, Handler, isRequest, Body, Fields>::
operator()(error_code ec)
{
auto& d = *d_;
if(ec)
goto upcall;
switch(d.state)
{
case 0:
d.state = 1;
write_some_op<Stream, decltype(d.sr),
write_op>{std::move(*this), d.s, d.sr}();
return;
case 1:
d.state = 2;
// [[fallthrough]]
case 2:
if(d.sr.is_done())
{
if(d.sr.needs_close())
// VFALCO Choose an error code
ec = boost::asio::error::eof;
break;
}
write_some_op<Stream, decltype(d.sr),
write_op>{std::move(*this), d.s, d.sr}();
return;
}
upcall:
if(! ec)
if(d.sr.need_close())
ec = error::end_of_stream;
d_.invoke(ec);
}
@ -258,7 +453,8 @@ class write_some_lambda
Stream& stream_;
public:
boost::optional<std::size_t> bytes_transferred;
bool invoked = false;
std::size_t bytes_transferred = 0;
explicit
write_some_lambda(Stream& stream)
@ -271,25 +467,55 @@ public:
operator()(error_code& ec,
ConstBufferSequence const& buffer)
{
invoked = true;
bytes_transferred =
stream_.write_some(buffer, ec);
}
};
template<class Stream>
class write_lambda
{
Stream& stream_;
public:
bool invoked = false;
std::size_t bytes_transferred = 0;
explicit
write_lambda(Stream& stream)
: stream_(stream)
{
}
template<class ConstBufferSequence>
void
operator()(error_code& ec,
ConstBufferSequence const& buffer)
{
invoked = true;
bytes_transferred = boost::asio::write(
stream_, buffer, ec);
}
};
} // detail
//------------------------------------------------------------------------------
template<
class SyncWriteStream,
bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
template<class SyncWriteStream, bool isRequest,
class Body, class Fields, class Decorator,
class Allocator>
void
write_some(SyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr)
{
static_assert(is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
static_assert(is_body<Body>::value,
"Body requirements not met");
static_assert(is_body_reader<Body>::value,
"BodyReader requirements not met");
error_code ec;
write_some(stream, sr, ec);
if(ec)
@ -307,15 +533,18 @@ write_some(SyncWriteStream& stream, serializer<
{
static_assert(is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
static_assert(is_body<Body>::value,
"Body requirements not met");
static_assert(is_body_reader<Body>::value,
"BodyReader requirements not met");
detail::write_some_lambda<SyncWriteStream> f{stream};
sr.get(ec, f);
if(! ec)
if(! sr.is_done())
{
if(f.bytes_transferred)
sr.consume(*f.bytes_transferred);
if(sr.is_done() && sr.needs_close())
// VFALCO decide on an error code
ec = boost::asio::error::eof;
sr.get(ec, f);
if(ec)
return;
if(f.invoked)
sr.consume(f.bytes_transferred);
}
}
@ -336,12 +565,159 @@ async_write_some(AsyncWriteStream& stream, serializer<
"BodyReader requirements not met");
async_completion<WriteHandler,
void(error_code)> init{handler};
detail::write_some_op<AsyncWriteStream, decltype(sr),
handler_type<WriteHandler, void(error_code)>>{
detail::write_some_op<AsyncWriteStream, isRequest,
Body, Fields, Decorator, Allocator, handler_type<
WriteHandler, void(error_code)>>{
init.completion_handler, stream, sr}();
return init.result.get();
}
//------------------------------------------------------------------------------
template<
class SyncWriteStream,
bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
void
write_header(SyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr)
{
static_assert(is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
static_assert(is_body<Body>::value,
"Body requirements not met");
static_assert(is_body_reader<Body>::value,
"BodyReader requirements not met");
error_code ec;
write_header(stream, sr, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
}
template<
class SyncWriteStream,
bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
void
write_header(SyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr,
error_code& ec)
{
static_assert(is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
static_assert(is_body<Body>::value,
"Body requirements not met");
static_assert(is_body_reader<Body>::value,
"BodyReader requirements not met");
sr.split(true);
detail::write_lambda<SyncWriteStream> f{stream};
while(! sr.is_header_done())
{
sr.get(ec, f);
if(ec)
return;
BOOST_ASSERT(f.invoked);
sr.consume(f.bytes_transferred);
}
}
template<class AsyncWriteStream,
bool isRequest, class Body, class Fields,
class Decorator, class Allocator, class WriteHandler>
async_return_type<WriteHandler, void(error_code)>
async_write_header(AsyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr,
WriteHandler&& handler)
{
static_assert(is_async_write_stream<
AsyncWriteStream>::value,
"AsyncWriteStream requirements not met");
static_assert(is_body<Body>::value,
"Body requirements not met");
static_assert(is_body_reader<Body>::value,
"BodyReader requirements not met");
sr.split(true);
async_completion<WriteHandler,
void(error_code)> init{handler};
detail::write_op<AsyncWriteStream, isRequest, Body, Fields,
Decorator, Allocator, detail::serializer_is_header_done,
handler_type<WriteHandler, void(error_code)>>{
init.completion_handler, stream, sr}(
error_code{}, 0);
return init.result.get();
}
//------------------------------------------------------------------------------
template<
class SyncWriteStream,
bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
void
write(SyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr)
{
static_assert(is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
error_code ec;
write(stream, sr, ec);
if(ec)
BOOST_THROW_EXCEPTION(system_error{ec});
}
template<
class SyncWriteStream,
bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
void
write(SyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr,
error_code& ec)
{
static_assert(is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
sr.split(false);
detail::write_lambda<SyncWriteStream> f{stream};
while(! sr.is_done())
{
sr.get(ec, f);
if(ec)
return;
if(f.invoked)
sr.consume(f.bytes_transferred);
}
if(sr.need_close())
ec = error::end_of_stream;
}
template<class AsyncWriteStream,
bool isRequest, class Body, class Fields,
class Decorator, class Allocator, class WriteHandler>
async_return_type<WriteHandler, void(error_code)>
async_write(AsyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr,
WriteHandler&& handler)
{
static_assert(is_async_write_stream<
AsyncWriteStream>::value,
"AsyncWriteStream requirements not met");
static_assert(is_body<Body>::value,
"Body requirements not met");
static_assert(is_body_reader<Body>::value,
"BodyReader requirements not met");
sr.split(false);
async_completion<WriteHandler,
void(error_code)> init{handler};
detail::write_op<AsyncWriteStream, isRequest, Body,
Fields, Decorator, Allocator, detail::serializer_is_done,
handler_type<WriteHandler, void(error_code)>>{
init.completion_handler, stream, sr}(
error_code{}, 0);
return init.result.get();
}
//------------------------------------------------------------------------------
template<class SyncWriteStream,
bool isRequest, class Body, class Fields>
void
@ -374,18 +750,10 @@ write(SyncWriteStream& stream,
static_assert(is_body_reader<Body>::value,
"BodyReader requirements not met");
auto sr = make_serializer(msg);
for(;;)
{
#if 0
sr.write_some(stream, ec);
#else
write_some(stream, sr, ec);
#endif
if(ec)
return;
if(sr.is_done())
break;
}
write(stream, sr, ec);
if(! ec)
if(sr.need_close())
ec = error::end_of_stream;
}
template<class AsyncWriteStream,
@ -406,11 +774,10 @@ async_write(AsyncWriteStream& stream,
"BodyReader requirements not met");
async_completion<WriteHandler,
void(error_code)> init{handler};
detail::write_op<AsyncWriteStream, handler_type<
detail::write_msg_op<AsyncWriteStream, handler_type<
WriteHandler, void(error_code)>,
isRequest, Body, Fields>{
init.completion_handler, stream, msg}(
error_code{});
init.completion_handler, stream, msg}();
return init.result.get();
}
@ -439,7 +806,7 @@ operator<<(std::ostream& os,
beast::detail::sync_ostream oss{os};
error_code ec;
write(oss, msg, ec);
if(ec && ec != boost::asio::error::eof)
if(ec && ec != error::end_of_stream)
BOOST_THROW_EXCEPTION(system_error{ec});
return os;
}

View File

@ -34,13 +34,18 @@ namespace http {
@note A new instance of the parser is required for each message.
*/
template<bool isRequest, class Body, class Fields>
template<bool isRequest, class Body, class Fields = fields>
class message_parser
: public basic_parser<isRequest,
Body::writer::is_direct,
message_parser<isRequest, Body, Fields>>
message_parser<isRequest, Body, Fields>>
{
using base_type = basic_parser<isRequest, true,
static_assert(is_body<Body>::value,
"Body requirements not met");
static_assert(is_body_writer<Body>::value,
"BodyWriter requirements not met");
using base_type = basic_parser<isRequest,
message_parser<isRequest, Body, Fields>>;
using writer_type = typename Body::writer;
@ -52,10 +57,6 @@ public:
/// The type of message returned by the parser
using value_type = message<isRequest, Body, Fields>;
/// The type of buffer sequence representing the body
using mutable_buffers_type =
typename writer_type::mutable_buffers_type;
/// Constructor (default)
message_parser() = default;
@ -99,9 +100,7 @@ public:
#endif
/** Construct a message parser from a @ref header_parser.
@param parser The header parser to construct from.
@param args Optional arguments forwarded to the message
constructor.
*/
@ -152,8 +151,7 @@ public:
private:
friend class basic_parser<
isRequest, Body::writer::is_direct,
message_parser>;
isRequest, message_parser>;
void
on_request(
@ -190,61 +188,25 @@ private:
}
void
on_body()
{
wr_.emplace(m_);
wr_->init();
}
void
on_body(std::uint64_t content_length)
{
wr_.emplace(m_);
wr_->init(content_length);
}
void
on_body(error_code& ec)
{
wr_.emplace(m_);
wr_->init(ec);
if(ec)
return;
}
void
on_body(std::uint64_t content_length,
error_code& ec)
on_body(boost::optional<
std::uint64_t> const& content_length,
error_code& ec)
{
wr_.emplace(m_);
wr_->init(content_length, ec);
if(ec)
return;
}
void
on_data(string_view const& s,
error_code& ec)
{
BOOST_STATIC_ASSERT(! Body::writer::is_direct);
wr_->write(s, ec);
}
mutable_buffers_type
on_prepare(std::size_t n)
{
return wr_->prepare(n);
wr_->put(boost::asio::buffer(
s.data(), s.size()), ec);
}
void
on_commit(std::size_t n)
{
wr_->commit(n);
}
void
on_chunk(std::uint64_t,
string_view const&,
on_chunk(
std::uint64_t, string_view const&,
error_code&)
{
}
@ -253,28 +215,18 @@ private:
on_complete(error_code& ec)
{
if(wr_)
do_on_complete(ec,
std::integral_constant<bool,
Body::writer::is_direct>{});
}
void
do_on_complete(
error_code& ec, std::true_type)
{
wr_->finish();
}
void
do_on_complete(
error_code& ec, std::false_type)
{
wr_->finish(ec);
if(ec)
return;
wr_->finish(ec);
}
};
/// A parser for producing HTTP/1 messages
template<class Body, class Fields = fields>
using request_parser = message_parser<true, Body, Fields>;
/// A parser for producing HTTP/1 messages
template<class Body, class Fields = fields>
using response_parser = message_parser<false, Body, Fields>;
} // http
} // beast

View File

@ -66,12 +66,12 @@ namespace http {
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
bool isRequest, class Derived>
std::size_t
read_some(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser);
basic_parser<isRequest, Derived>& parser);
/** Read some HTTP/1 message data from a stream.
@ -122,12 +122,12 @@ read_some(
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
bool isRequest, class Derived>
std::size_t
read_some(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
error_code& ec);
/** Start an asynchronous operation to read some HTTP/1 message data from a stream.
@ -192,7 +192,7 @@ read_some(
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived,
bool isRequest, class Derived,
class ReadHandler>
#if BEAST_DOXYGEN
void_or_deduced
@ -203,7 +203,170 @@ async_return_type<
async_read_some(
AsyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
ReadHandler&& handler);
//------------------------------------------------------------------------------
/** Read a header into an HTTP/1 parser 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 is true:
@li The parser indicates that the complete header is received
@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 dynamic
buffer, which may be used in subsequent calls.
If the end of the stream is reached during the read, the
value @ref error::partial_message is indicated as the
error if bytes have been processed, else the error
@ref error::end_of_stream is indicated.
@param stream The stream from which the data is to be read.
The type must support the @b SyncReadStream concept.
@param buffer 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
dynamic buffer's input sequence will be given to the parser
first.
@param parser The parser to use.
@throws system_error Thrown on failure.
@note The implementation will call @ref basic_parser::eager
with the value `false` on the parser passed in.
*/
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived>
void
read_header(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser);
/** Read a header into an HTTP/1 parser 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 is true:
@li The parser indicates that the complete header is received
@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 dynamic
buffer, which may be used in subsequent calls.
If the end of the stream is reached during the read, the
value @ref error::partial_message is indicated as the
error if bytes have been processed, else the error
@ref error::end_of_stream is indicated.
@param stream The stream from which the data is to be read.
The type must support the @b SyncReadStream concept.
@param buffer 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
dynamic buffer's input sequence will be given to the parser
first.
@param parser The parser to use.
@param ec Set to the error, if any occurred.
@note The implementation will call @ref basic_parser::eager
with the value `false` on the parser passed in.
*/
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived>
void
read_header(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser,
error_code& ec);
/** Read a header into an HTTP/1 parser asynchronously 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 the complete header is received
@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.
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.
If the end of the stream is reached during the read, the
value @ref error::partial_message is indicated as the
error if bytes have been processed, else the error
@ref error::end_of_stream is indicated.
@param stream The stream from which the data is to be read.
The type must support the @b AsyncReadStream concept.
@param buffer 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
dynamic buffer's input sequence will be given to the parser
first.
@param parser The parser to use.
@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`.
@note The implementation will call @ref basic_parser::eager
with the value `false` on the parser passed in.
*/
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, class Derived,
class ReadHandler>
#if BEAST_DOXYGEN
void_or_deduced
#else
async_return_type<
ReadHandler, void(error_code)>
#endif
async_read_header(
AsyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, Derived>& parser,
ReadHandler&& handler);
//------------------------------------------------------------------------------
@ -241,16 +404,19 @@ async_read_some(
@param parser The parser to use.
@throws system_error Thrown on failure.
@note The implementation will call @ref basic_parser::eager
with the value `true` on the parser passed in.
*/
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
bool isRequest, class Derived>
void
read(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser);
basic_parser<isRequest, Derived>& parser);
/** Read into an HTTP/1 parser from a stream.
@ -285,16 +451,19 @@ read(
@param parser The parser to use.
@param ec Set to the error, if any occurred.
@note The implementation will call @ref basic_parser::eager
with the value `true` on the parser passed in.
*/
template<
class SyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived>
bool isRequest, class Derived>
void
read(
SyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
error_code& ec);
/** Read into an HTTP/1 parser asynchronously from a stream.
@ -342,11 +511,14 @@ read(
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`.
@note The implementation will call @ref basic_parser::eager
with the value `true` on the parser passed in.
*/
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest, bool isDirect, class Derived,
bool isRequest, class Derived,
class ReadHandler>
#if BEAST_DOXYGEN
void_or_deduced
@ -357,9 +529,11 @@ async_return_type<
async_read(
AsyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
ReadHandler&& handler);
//------------------------------------------------------------------------------
/** Read an HTTP/1 message from a stream.
This function is used to synchronously read a message from
@ -522,7 +696,6 @@ async_read(
} // http
} // beast
#include <beast/http/impl/async_read.ipp>
#include <beast/http/impl/read.ipp>
#endif

View File

@ -65,7 +65,7 @@ struct empty_decorator
if the contents of the message indicate that chunk encoding
is required. If the semantics of the message indicate that
the connection should be closed after the message is sent, the
function @ref needs_close will return `true`.
function @ref need_close will return `true`.
Upon construction, an optional chunk decorator may be
specified. This decorator is a function object called with
@ -135,9 +135,11 @@ class serializer
enum
{
do_init = 0,
do_header_only = 10,
do_header = 20,
do_construct = 0,
do_init = 10,
do_header_only = 20,
do_header = 30,
do_body = 40,
do_init_c = 50,
@ -191,7 +193,7 @@ class serializer
buffer_type b_;
boost::variant<boost::blank,
cb0_t, cb1_t, ch0_t, ch1_t, ch2_t> v_;
int s_;
int s_ = do_construct;
bool split_ = is_deferred::value;
bool header_done_ = false;
bool chunked_;
@ -201,8 +203,13 @@ class serializer
public:
/** Constructor
@param msg The message to serialize, which must
remain valid for the lifetime of the serializer.
The implementation guarantees that the message passed on
construction will not be accessed until the first call to
@ref get. This allows the message to be lazily created.
For example, if the header is filled in before serialization.
@param msg The message to serialize, which must remain valid
for the lifetime of the serializer.
@param decorator An optional decorator to use.
@ -268,7 +275,7 @@ public:
function returns `true`.
*/
bool
needs_close() const
need_close() const
{
return close_;
}

View File

@ -9,7 +9,7 @@
#define BEAST_HTTP_STRING_BODY_HPP
#include <beast/config.hpp>
#include <beast/core/error.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
@ -30,68 +30,6 @@ struct string_body
/// The type of the body member when used in a message.
using value_type = std::string;
#if BEAST_DOXYGEN
/// The algorithm used store buffers in this body
using writer = implementation_defined;
#else
class writer
{
value_type& body_;
std::size_t len_ = 0;
public:
static bool constexpr is_direct = true;
using mutable_buffers_type =
boost::asio::mutable_buffers_1;
template<bool isRequest, class Fields>
explicit
writer(message<isRequest,
string_body, Fields>& m)
: body_(m.body)
{
}
void
init()
{
}
void
init(std::uint64_t content_length)
{
if(content_length >
(std::numeric_limits<std::size_t>::max)())
BOOST_THROW_EXCEPTION(std::length_error{
"Content-Length overflow"});
body_.reserve(static_cast<
std::size_t>(content_length));
}
mutable_buffers_type
prepare(std::size_t n)
{
body_.resize(len_ + n);
return {&body_[len_], n};
}
void
commit(std::size_t n)
{
if(body_.size() > len_ + n)
body_.resize(len_ + n);
len_ = body_.size();
}
void
finish()
{
body_.resize(len_);
}
};
#endif
#if BEAST_DOXYGEN
/// The algorithm to obtain buffers representing the body
using reader = implementation_defined;
@ -134,6 +72,69 @@ struct string_body
}
};
#endif
#if BEAST_DOXYGEN
/// The algorithm used store buffers in this body
using writer = implementation_defined;
#else
class writer
{
value_type& body_;
public:
template<bool isRequest, class Fields>
explicit
writer(message<isRequest, string_body, Fields>& m)
: body_(m.body)
{
}
void
init(boost::optional<
std::uint64_t> content_length, error_code& ec)
{
if(content_length)
{
if(*content_length > (std::numeric_limits<
std::size_t>::max)())
{
ec = make_error_code(
errc::not_enough_memory);
return;
}
body_.reserve(static_cast<
std::size_t>(*content_length));
}
}
template<class ConstBufferSequence>
void
put(ConstBufferSequence const& buffers,
error_code& ec)
{
using boost::asio::buffer_size;
using boost::asio::buffer_copy;
auto const n = buffer_size(buffers);
auto const len = body_.size();
try
{
body_.resize(len + n);
}
catch(std::length_error const&)
{
ec = error::buffer_overflow;
return;
}
buffer_copy(boost::asio::buffer(
&body_[0] + len, n), buffers);
}
void
finish(error_code&)
{
}
};
#endif
};
} // http

View File

@ -114,28 +114,24 @@ struct is_body_reader<T, beast::detail::void_t<
template<class T>
struct is_body_writer : std::integral_constant<bool, ...> {};
#else
template<class T, class = beast::detail::void_t<>>
struct is_body_writer : std::true_type {};
template<class T, class = void>
struct is_body_writer : std::false_type {};
template<class T>
struct is_body_writer<T, beast::detail::void_t<decltype(
std::declval<typename T::writer::mutable_buffers_type>(),
std::declval<T::writer>().init(
std::declval<boost::optional<std::uint64_t>>()),
std::declval<T::writer>().prepare(
std::declval<std::size_t>()),
std::declval<T::writer>().commit(
std::declval<std::size_t>()),
std::declval<T::writer>().finish()
)> > : std::integral_constant<bool,
is_mutable_buffer_sequence<
typename T::writer::mutable_buffers_type>::value &&
std::is_convertible<decltype(
std::declval<T::writer>().prepare(
std::declval<std::size_t>())),
typename T::writer::mutable_buffers_type
>::value>
std::declval<typename T::writer&>().init(
std::declval<boost::optional<std::uint64_t>>(),
std::declval<error_code&>()),
std::declval<typename T::writer&>().put(
std::declval<boost::asio::const_buffers_1>(),
std::declval<error_code&>()),
std::declval<typename T::writer&>().finish(
std::declval<error_code&>()),
(void)0)>> : std::integral_constant<bool,
true
&& std::is_constructible<typename T::writer,
message<true, T, detail::fields_model>&>::value
>
{
};
#endif

View File

@ -28,27 +28,35 @@
namespace beast {
namespace http {
/** Write some serialized message data to a stream.
/** Write part of a message to a stream using a serializer.
This function is used to write serialized message data to the
stream. The function call will block until one of the following
conditions is true:
This function is used to write part of a message to a stream using
a caller-provided HTTP/1 serializer. The call will block until one
of the following conditions is true:
@li One or more bytes have been transferred.
@li The function @ref serializer::is_done returns `true`
@li An error occurs on the stream.
In order to completely serialize a message, this function
should be called until `sr.is_done()` returns `true`.
This operation is implemented in terms of one or more calls
to the stream's `write_some` function.
@param stream The stream to write to. This type must
satisfy the requirements of @b SyncWriteStream.
The amount of data actually transferred is controlled by the behavior
of the underlying stream, performing bounded work for each call. This
helps applications set reasonable timeouts. It also allows application-level
flow control to function correctly. For example when using a TCP/IP based
stream.
@param stream The stream to which the data is to be written.
The type must support the @b SyncWriteStream concept.
@param sr The serializer to use.
@throws system_error Thrown on failure.
@see @ref async_write_some, @ref serializer
@see serializer
*/
template<class SyncWriteStream,
bool isRequest, class Body, class Fields,
@ -57,22 +65,29 @@ void
write_some(SyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr);
/** Write part of a message to a stream using a serializer.
/** Write some serialized message data to a stream.
This function is used to write serialized message data to the
stream. The function call will block until one of the following
conditions is true:
This function is used to write part of a message to a stream using
a caller-provided HTTP/1 serializer. The call will block until one
of the following conditions is true:
@li One or more bytes have been transferred.
@li The function @ref serializer::is_done returns `true`
@li An error occurs on the stream.
In order to completely serialize a message, this function
should be called until `sr.is_done()` returns `true`.
This operation is implemented in terms of one or more calls
to the stream's `write_some` function.
@param stream The stream to write to. This type must
satisfy the requirements of @b SyncWriteStream.
The amount of data actually transferred is controlled by the behavior
of the underlying stream, performing bounded work for each call. This
helps applications set reasonable timeouts. It also allows application-level
flow control to function correctly. For example when using a TCP/IP based
stream.
@param stream The stream to which the data is to be written.
The type must support the @b SyncWriteStream concept.
@param sr The serializer to use.
@ -88,37 +103,47 @@ write_some(SyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr,
error_code& ec);
/** Start an asynchronous write of some serialized message data to a stream.
/** Write part of a message to a stream asynchronously using a serializer.
This function is used to asynchronously write serialized
message data to the stream. The function call always returns
immediately. The asynchronous operation will continue until
one of the following conditions is true:
This function is used to write part of a message to a stream
asynchronously using a caller-provided HTTP/1 serializer. The function
call always returns immediately. The asynchronous operation will continue
until one of the following conditions is true:
@li One or more bytes have been transferred.
@li The function @ref serializer::is_done returns `true`
@li An error occurs on the stream.
In order to completely serialize a message, this function
should be called until `sr.is_done()` returns `true`.
This operation is implemented in terms of zero or more calls to the stream's
`async_write_some` function, and is known as a <em>composed operation</em>.
The program must ensure that the stream performs no other write operations
until this operation completes.
@param stream The stream to write to. This type must
satisfy the requirements of @b SyncWriteStream.
The amount of data actually transferred is controlled by the behavior
of the underlying stream, performing bounded work for each call. This
helps applications set reasonable timeouts. It also allows application-level
flow control to function correctly. For example when using a TCP/IP based
stream.
@param sr The serializer to use for writing.
@param stream The stream to which the data is to be written.
The type must support the @b AsyncWriteStream concept.
@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:
@param sr The serializer to use.
@param handler The handler to be called when the operation
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& ec // Result of operation
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`.
@see @ref write_some, @ref serializer
@see @ref serializer
*/
template<class AsyncWriteStream,
bool isRequest, class Body, class Fields,
@ -132,98 +157,93 @@ async_write_some(AsyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr,
WriteHandler&& handler);
/** Write an HTTP/1 message to a stream.
//------------------------------------------------------------------------------
This function is used to write a message to a stream. The call
will block until one of the following conditions is true:
/** Write a header to a stream using a serializer.
@li The entire message is written.
This function is used to write a header to a stream using a
caller-provided HTTP/1 serializer. The call will block until one
of the following conditions is true:
@li The function @ref serializer::is_header_done returns `true`
@li An error occurs.
This operation is implemented in terms of one or more calls
to the stream's `write_some` function.
The implementation will automatically perform chunk encoding if
the contents of the message indicate that chunk encoding is required.
If the semantics of the message indicate that the connection should
be closed after the message is sent, the error thrown from this
function will be `boost::asio::error::eof`.
@param stream The stream to which the data is to be written.
The type must support the @b SyncWriteStream concept.
@param msg The message to write.
@param sr The serializer to use.
@throws system_error Thrown on failure.
@note The implementation will call @ref serializer::split with
the value `true` on the serializer passed in.
@see @ref serializer
*/
template<class SyncWriteStream,
bool isRequest, class Body, class Fields>
bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
void
write(SyncWriteStream& stream,
message<isRequest, Body, Fields> const& msg);
write_header(SyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr);
/** Write an HTTP/1 message to a stream.
/** Write a header to a stream using a serializer.
This function is used to write a message to a stream. The call
will block until one of the following conditions is true:
This function is used to write a header to a stream using a
caller-provided HTTP/1 serializer. The call will block until one
of the following conditions is true:
@li The entire message is written.
@li The function @ref serializer::is_header_done returns `true`
@li An error occurs.
This operation is implemented in terms of one or more calls
to the stream's `write_some` function.
The implementation will automatically perform chunk encoding if
the contents of the message indicate that chunk encoding is required.
If the semantics of the message indicate that the connection should
be closed after the message is sent, the error returned from this
function will be `boost::asio::error::eof`.
@param stream The stream to which the data is to be written.
The type must support the @b SyncWriteStream concept.
@param msg The message to write.
@param sr The serializer to use.
@param ec Set to the error, if any occurred.
@param ec Set to indicate what error occurred, if any.
@note The implementation will call @ref serializer::split with
the value `true` on the serializer passed in.
@see @ref serializer
*/
template<class SyncWriteStream,
bool isRequest, class Body, class Fields>
bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
void
write(SyncWriteStream& stream,
message<isRequest, Body, Fields> const& msg,
write_header(SyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr,
error_code& ec);
/** Write an HTTP/1 message asynchronously to a stream.
/** Write a header to a stream asynchronously using a serializer.
This function is used to asynchronously write a message to
a stream. The function call always returns immediately. The
asynchronous operation will continue until one of the following
conditions is true:
This function is used to write a header to a stream asynchronously
using a caller-provided HTTP/1 serializer. The function call always
returns immediately. The asynchronous operation will continue until
one of the following conditions is true:
@li The entire message is written.
@li The function @ref serializer::is_header_done returns `true`
@li An error occurs.
This operation is implemented in terms of one or more calls to
the stream's `async_write_some` functions, and is known as a
<em>composed operation</em>. The program must ensure that the
stream performs no other write operations until this operation
completes.
The implementation will automatically perform chunk encoding if
the contents of the message indicate that chunk encoding is required.
If the semantics of the message indicate that the connection should
be closed after the message is sent, the operation will complete with
the error set to `boost::asio::error::eof`.
This operation is implemented in terms of zero or more calls to the stream's
`async_write_some` function, and is known as a <em>composed operation</em>.
The program must ensure that the stream performs no other write operations
until this operation completes.
@param stream The stream to which the data is to be written.
The type must support the @b AsyncWriteStream concept.
@param msg The message to write. The object must remain valid
at least until the completion handler is called; ownership is
not transferred.
@param sr The serializer to use.
@param handler The handler to be called when the operation
completes. Copies will be made of the handler as required.
@ -235,6 +255,231 @@ write(SyncWriteStream& stream,
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`.
@note The implementation will call @ref serializer::split with
the value `true` on the serializer passed in.
@see @ref serializer
*/
template<class AsyncWriteStream,
bool isRequest, class Body, class Fields,
class Decorator, class Allocator, class WriteHandler>
#if BEAST_DOXYGEN
void_or_deduced
#else
async_return_type<WriteHandler, void(error_code)>
#endif
async_write_header(AsyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr,
WriteHandler&& handler);
//------------------------------------------------------------------------------
/** Write a complete message to a stream using a serializer.
This function is used to write a complete message to a stream using
a caller-provided HTTP/1 serializer. The call will block until one
of the following conditions is true:
@li The function @ref serializer::is_done returns `true`
@li An error occurs.
This operation is implemented in terms of one or more calls
to the stream's `write_some` function.
@param stream The stream to which the data is to be written.
The type must support the @b SyncWriteStream concept.
@param sr The serializer to use.
@throws system_error Thrown on failure.
@see @ref serializer
*/
template<class SyncWriteStream,
bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
void
write(SyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr);
/** Write a complete message to a stream using a serializer.
This function is used to write a complete message to a stream using
a caller-provided HTTP/1 serializer. The call will block until one
of the following conditions is true:
@li The function @ref serializer::is_done returns `true`
@li An error occurs.
This operation is implemented in terms of one or more calls
to the stream's `write_some` function.
@param stream The stream to which the data is to be written.
The type must support the @b SyncWriteStream concept.
@param sr The serializer to use.
@param ec Set to the error, if any occurred.
@see @ref serializer
*/
template<class SyncWriteStream,
bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
void
write(SyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr,
error_code& ec);
/** Write a complete message to a stream asynchronously using a serializer.
This function is used to write a complete message to a stream
asynchronously using a caller-provided HTTP/1 serializer. The
function call always returns immediately. The asynchronous
operation will continue until one of the following conditions is true:
@li The function @ref serializer::is_done returns `true`
@li An error occurs.
This operation is implemented in terms of zero or more calls to the stream's
`async_write_some` function, and is known as a <em>composed operation</em>.
The program must ensure that the stream performs no other write operations
until this operation completes.
@param stream The stream to which the data is to be written.
The type must support the @b AsyncWriteStream concept.
@param sr The serializer to use.
@param handler The handler to be called when the operation
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`.
@see @ref serializer
*/
template<class AsyncWriteStream,
bool isRequest, class Body, class Fields,
class Decorator, class Allocator, class WriteHandler>
#if BEAST_DOXYGEN
void_or_deduced
#else
async_return_type<WriteHandler, void(error_code)>
#endif
async_write(AsyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr,
WriteHandler&& handler);
//------------------------------------------------------------------------------
/** Write a complete message to a stream.
This function is used to write a complete message to a stream using
HTTP/1. The call will block until one of the following conditions is true:
@li The entire message is written.
@li An error occurs.
This operation is implemented in terms of one or more calls to the stream's
`write_some` function. The algorithm will use a temporary @ref serializer
with an empty chunk decorator to produce buffers. If the semantics of the
message indicate that the connection should be closed after the message is
sent, the error delivered by this function will be @ref error::end_of_stream
@param stream The stream to which the data is to be written.
The type must support the @b SyncWriteStream concept.
@param msg The message to write.
@throws system_error Thrown on failure.
@see @ref message
*/
template<class SyncWriteStream,
bool isRequest, class Body, class Fields>
void
write(SyncWriteStream& stream,
message<isRequest, Body, Fields> const& msg);
/** Write a complete message to a stream.
This function is used to write a complete message to a stream using
HTTP/1. The call will block until one of the following conditions is true:
@li The entire message is written.
@li An error occurs.
This operation is implemented in terms of one or more calls to the stream's
`write_some` function. The algorithm will use a temporary @ref serializer
with an empty chunk decorator to produce buffers. If the semantics of the
message indicate that the connection should be closed after the message is
sent, the error delivered by this function will be @ref error::end_of_stream
@param stream The stream to which the data is to be written.
The type must support the @b SyncWriteStream concept.
@param msg The message to write.
@param ec Set to the error, if any occurred.
@see @ref message
*/
template<class SyncWriteStream,
bool isRequest, class Body, class Fields>
void
write(SyncWriteStream& stream,
message<isRequest, Body, Fields> const& msg,
error_code& ec);
/** Write a complete message to a stream asynchronously.
This function is used to write a complete message to a stream asynchronously
using HTTP/1. The function call always returns immediately. The asynchronous
operation will continue until one of the following conditions is true:
@li The entire message is written.
@li An error occurs.
This operation is implemented in terms of zero or more calls to the stream's
`async_write_some` function, and is known as a <em>composed operation</em>.
The program must ensure that the stream performs no other write operations
until this operation completes. The algorithm will use a temporary
@ref serializer with an empty chunk decorator to produce buffers. If
the semantics of the message indicate that the connection should be
closed after the message is sent, the error delivered by this function
will be @ref error::end_of_stream
@param stream The stream to which the data is to be written.
The type must support the @b AsyncWriteStream concept.
@param msg The message to write. The object must remain valid at least
until the completion handler is called; ownership is not transferred.
@param handler The handler to be called when the operation
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`.
@see @ref message
*/
template<class AsyncWriteStream,
bool isRequest, class Body, class Fields,

View File

@ -20,7 +20,7 @@ namespace websocket {
namespace detail {
// A container that holds a suspended, asynchronous composed
// operation. The contained object may be invoked later to
// operation. The contained object may be invoked later to
// resume the operation, or the container may be destroyed.
//
class pausation

View File

@ -281,7 +281,7 @@ operator()(error_code ec,
case 1:
{
BOOST_ASSERT(d.p.got_header());
BOOST_ASSERT(d.p.is_header_done());
d.ws.stream_.buffer().consume(bytes_used);
// Arguments from our state must be
// moved to the stack before releasing

View File

@ -96,7 +96,7 @@ do_accept(
next_layer(), stream_.buffer(), p, ec);
if(ec)
return;
BOOST_ASSERT(p.got_header());
BOOST_ASSERT(p.is_header_done());
stream_.buffer().consume(bytes_used);
do_accept(p.get(), decorator, ec);
}

View File

@ -8,6 +8,7 @@
// Test that header file is self-contained.
#include <beast/core/bind_handler.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <beast/unit_test/suite.hpp>
#include <functional>
@ -16,6 +17,21 @@ namespace beast {
class bind_handler_test : public unit_test::suite
{
public:
struct handler
{
void
operator()() const;
};
#if 0
// This function should fail to compile
void
failStdBind()
{
std::bind(bind_handler(handler{}));
}
#endif
void
callback(int v)
{

View File

@ -40,6 +40,7 @@ add_executable (http-bench
${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
nodejs_parser.hpp
message_fuzz.hpp
../../extras/beast/unit_test/main.cpp
nodejs_parser.cpp
parser_bench.cpp

View File

@ -148,19 +148,18 @@ public:
return {s, N-1};
}
template<
bool isRequest, bool isDirect, class Derived>
template<bool isRequest, class Derived>
static
std::size_t
feed(boost::asio::const_buffer buffer,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
using boost::asio::const_buffers_1;
std::size_t used = 0;
for(;;)
{
auto const n = parser.write(
auto const n = parser.put(
const_buffers_1{buffer}, ec);
if(ec)
return 0;
@ -168,7 +167,7 @@ public:
break;
buffer = buffer + n;
used += n;
if(parser.is_complete())
if(parser.is_done())
break;
if(buffer_size(buffer) == 0)
break;
@ -177,11 +176,11 @@ public:
}
template<class ConstBufferSequence,
bool isRequest, bool isDirect, class Derived>
bool isRequest, class Derived>
static
std::size_t
feed(ConstBufferSequence const& buffers,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
using boost::asio::buffer_size;
@ -191,14 +190,14 @@ public:
for(;;)
{
auto const n =
parser.write(cb, ec);
parser.put(cb, ec);
if(ec)
return 0;
if(n == 0)
break;
cb.consume(n);
used += n;
if(parser.is_complete())
if(parser.is_done())
break;
if(buffer_size(cb) == 0)
break;
@ -206,12 +205,11 @@ public:
return used;
}
template<
bool isRequest, bool isDirect, class Derived>
template<bool isRequest, class Derived>
static
std::size_t
feed(boost::asio::const_buffers_1 buffers,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
return feed(*buffers.begin(), parser, ec);
@ -225,7 +223,7 @@ public:
using boost::asio::buffer;
test_parser<isRequest> p;
if(skipBody)
p.skip_body();
p.skip(true);
error_code ec;
auto const n = feed(buffer(
s.data(), s.size()), p, ec);
@ -233,8 +231,8 @@ public:
return;
if(! BEAST_EXPECT(n == s.size()))
return;
if(p.state() == parse_state::body_to_eof)
p.write_eof(ec);
if(p.need_eof())
p.put_eof(ec);
if(BEAST_EXPECTS(! ec, ec.message()))
pred(p);
}
@ -257,18 +255,19 @@ public:
using boost::asio::buffer;
test_parser<isRequest> p;
if(skipBody)
p.skip_body();
p.skip(true);
error_code ec;
feed(buffer(
s.data(), s.size()), p, ec);
if(! ec && ev)
p.write_eof(ec);
p.put_eof(ec);
BEAST_EXPECTS(ec == ev, ec.message());
}
void
testFlatten()
{
#if 0
using boost::asio::buffer;
{
std::string const s =
@ -289,7 +288,7 @@ public:
BEAST_EXPECTS(! ec, ec.message());
feed(buffer_cat(b1, b2), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
}
{
@ -310,9 +309,10 @@ public:
ec = {};
feed(buffer_cat(b1, b2), p, ec);
BEAST_EXPECTS(! ec, ec.message());
p.write_eof(ec);
p.put_eof(ec);
}
}
#endif
}
// Check that all callbacks are invoked
@ -331,7 +331,7 @@ public:
"*";
feed(buffer(s), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
BEAST_EXPECT(p.got_on_begin);
BEAST_EXPECT(p.got_on_field);
BEAST_EXPECT(p.got_on_header);
@ -350,7 +350,7 @@ public:
"*";
feed(buffer(s), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
BEAST_EXPECT(p.got_on_begin);
BEAST_EXPECT(p.got_on_field);
BEAST_EXPECT(p.got_on_header);
@ -747,6 +747,7 @@ public:
void testBody()
{
#if 0
using boost::asio::buffer;
good<true>(
"GET / HTTP/1.1\r\n"
@ -774,7 +775,7 @@ public:
buf("67890")),
p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
// request without Content-Length or
@ -787,7 +788,7 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
{
error_code ec;
@ -797,7 +798,7 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
// response without Content-Length or
@ -810,17 +811,17 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(! p.is_complete());
BEAST_EXPECT(! p.is_done());
BEAST_EXPECT(p.state() == parse_state::body_to_eof);
feed(buf(
"hello"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(! p.is_complete());
BEAST_EXPECT(! p.is_done());
BEAST_EXPECT(p.state() == parse_state::body_to_eof);
p.write_eof(ec);
p.put_eof(ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
// 304 "Not Modified" response does not require eof
@ -832,7 +833,7 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
// Chunked response does not require eof
@ -845,12 +846,12 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(! p.is_complete());
BEAST_EXPECT(! p.is_done());
feed(buf(
"0\r\n\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
// restart: 1.0 assumes Connection: close
@ -862,7 +863,7 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
// restart: 1.1 assumes Connection: keep-alive
@ -874,7 +875,7 @@ public:
"\r\n"
), p, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
}
bad<true>(
@ -882,8 +883,10 @@ public:
"Content-Length: 1\r\n"
"\r\n",
error::partial_message);
#endif
}
#if 0
template<bool isRequest>
void
check_header(
@ -898,6 +901,7 @@ public:
BEAST_EXPECT(! p.got_on_complete);
BEAST_EXPECT(p.state() != parse_state::header);
}
#endif
void
testSplit()
@ -921,7 +925,7 @@ public:
BEAST_EXPECT(! p.got_on_chunk);
BEAST_EXPECT(! p.got_on_complete);
BEAST_EXPECT(p.state() != parse_state::header);
BEAST_EXPECT(! p.is_complete());
BEAST_EXPECT(! p.is_done());
BEAST_EXPECT(p.body.empty());
b.consume(n);
p.resume();
@ -933,7 +937,7 @@ public:
BEAST_EXPECT(p.got_on_body);
BEAST_EXPECT(! p.got_on_chunk);
BEAST_EXPECT(p.got_on_complete);
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
BEAST_EXPECT(p.body == "*****");
#endif
}

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,13 @@ class header_parser_test
, public test::enable_yield_to
{
public:
static
boost::asio::const_buffers_1
buf(string_view s)
{
return {s.data(), s.size()};
}
void
testParse()
{
@ -34,7 +41,7 @@ public:
flat_buffer db{1024};
header_parser<true, fields> p;
read_some(is, db, p);
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_header_done());
}
{
test::string_istream is{ios_,
@ -47,8 +54,8 @@ public:
flat_buffer db{1024};
header_parser<true, fields> p;
read_some(is, db, p);
BEAST_EXPECT(! p.is_complete());
BEAST_EXPECT(p.state() == parse_state::body);
BEAST_EXPECT(p.is_header_done());
BEAST_EXPECT(! p.is_done());
}
}

View File

@ -14,10 +14,10 @@
#include <beast/test/string_istream.hpp>
#include <beast/test/string_ostream.hpp>
#include <beast/test/yield_to.hpp>
#include <beast/core/consuming_buffers.hpp>
#include <beast/core/flat_buffer.hpp>
#include <beast/core/multi_buffer.hpp>
#include <beast/http/header_parser.hpp>
#include <beast/http/read.hpp>
#include <beast/core/ostream.hpp>
#include <beast/http/read.hpp>
#include <beast/http/string_body.hpp>
#include <boost/system/system_error.hpp>
@ -30,41 +30,117 @@ class message_parser_test
, public beast::test::enable_yield_to
{
public:
template<bool isRequest, class Pred>
void
testMatrix(std::string const& s, Pred const& pred)
template<bool isRequest>
using parser_type =
message_parser<isRequest, string_body, fields>;
static
boost::asio::const_buffers_1
buf(string_view s)
{
beast::test::string_istream ss{get_io_service(), s};
error_code ec;
#if 0
multi_buffer buffer;
#else
flat_buffer buffer{1024};
#endif
message<isRequest, string_body, fields> m;
read(ss, buffer, m, ec);
if(! BEAST_EXPECTS(! ec, ec.message()))
return;
pred(m);
return {s.data(), s.size()};
}
template<class ConstBufferSequence,
bool isRequest, class Derived>
static
void
put(ConstBufferSequence const& buffers,
basic_parser<isRequest, Derived>& p,
error_code& ec)
{
using boost::asio::buffer_size;
consuming_buffers<ConstBufferSequence> cb{buffers};
for(;;)
{
auto const used = p.put(cb, ec);
cb.consume(used);
if(ec)
return;
if(p.need_eof() &&
buffer_size(cb) == 0)
{
p.put_eof(ec);
if(ec)
return;
}
if(p.is_done())
break;
}
}
template<bool isRequest, class F>
void
doMatrix(string_view s0, F const& f)
{
using boost::asio::buffer;
// parse a single buffer
{
auto s = s0;
error_code ec;
parser_type<isRequest> p;
put(buffer(s.data(), s.size()), p, ec);
if(! BEAST_EXPECTS(! ec, ec.message()))
return;
f(p);
}
// parse two buffers
for(auto n = s0.size() - 1; n >= 1; --n)
{
auto s = s0;
error_code ec;
parser_type<isRequest> p;
p.eager(true);
auto used =
p.put(buffer(s.data(), n), ec);
s.remove_prefix(used);
if(ec == error::need_more)
ec = {};
if(! BEAST_EXPECTS(! ec, ec.message()))
continue;
BEAST_EXPECT(! p.is_done());
used = p.put(
buffer(s.data(), s.size()), ec);
s.remove_prefix(used);
if(! BEAST_EXPECTS(! ec, ec.message()))
continue;
BEAST_EXPECT(s.empty());
if(p.need_eof())
{
p.put_eof(ec);
if(! BEAST_EXPECTS(! ec, ec.message()))
continue;
}
if(BEAST_EXPECT(p.is_done()))
f(p);
}
}
void
testRead()
testParse()
{
testMatrix<false>(
doMatrix<false>(
"HTTP/1.0 200 OK\r\n"
"Server: test\r\n"
"\r\n"
"*******",
[&](message<false, string_body, fields> const& m)
"Hello, world!",
[&](parser_type<false> const& p)
{
BEAST_EXPECTS(m.body == "*******",
"body='" + m.body + "'");
auto const& m = p.get();
BEAST_EXPECT(! p.is_chunked());
BEAST_EXPECT(p.need_eof());
BEAST_EXPECT(p.content_length() == boost::none);
BEAST_EXPECT(m.version == 10);
BEAST_EXPECT(m.status == 200);
BEAST_EXPECT(m.reason() == "OK");
BEAST_EXPECT(m.fields["Server"] == "test");
BEAST_EXPECT(m.body == "Hello, world!");
}
);
testMatrix<false>(
"HTTP/1.0 200 OK\r\n"
doMatrix<false>(
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Expect: Expires, MD5-Fingerprint\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"5\r\n"
@ -75,132 +151,137 @@ public:
"Expires: never\r\n"
"MD5-Fingerprint: -\r\n"
"\r\n",
[&](message<false, string_body, fields> const& m)
[&](parser_type<false> const& p)
{
auto const& m = p.get();
BEAST_EXPECT(! p.need_eof());
BEAST_EXPECT(p.is_chunked());
BEAST_EXPECT(p.content_length() == boost::none);
BEAST_EXPECT(m.version == 11);
BEAST_EXPECT(m.status == 200);
BEAST_EXPECT(m.reason() == "OK");
BEAST_EXPECT(m.fields["Server"] == "test");
BEAST_EXPECT(m.fields["Transfer-Encoding"] == "chunked");
BEAST_EXPECT(m.fields["Expires"] == "never");
BEAST_EXPECT(m.fields["MD5-Fingerprint"] == "-");
BEAST_EXPECT(m.body == "*****--");
}
);
testMatrix<false>(
doMatrix<false>(
"HTTP/1.0 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 5\r\n"
"\r\n"
"*****",
[&](message<false, string_body, fields> const& m)
[&](parser_type<false> const& p)
{
auto const& m = p.get();
BEAST_EXPECT(m.body == "*****");
}
);
testMatrix<true>(
doMatrix<true>(
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"\r\n",
[&](message<true, string_body, fields> const& m)
[&](parser_type<true> const& p)
{
auto const& m = p.get();
BEAST_EXPECT(m.method() == "GET");
BEAST_EXPECT(m.target() == "/");
BEAST_EXPECT(m.version == 11);
BEAST_EXPECT(! p.need_eof());
BEAST_EXPECT(! p.is_chunked());
BEAST_EXPECT(p.content_length() == boost::none);
}
);
testMatrix<true>(
doMatrix<true>(
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"X: \t x \t \r\n"
"\r\n",
[&](message<true, string_body, fields> const& m)
[&](parser_type<true> const& p)
{
auto const& m = p.get();
BEAST_EXPECT(m.fields["X"] == "x");
}
);
}
void
testParse()
{
using boost::asio::buffer;
// test eager(true)
{
error_code ec;
beast::test::string_istream is{
get_io_service(),
parser_type<true> p;
p.eager(true);
p.put(buf(
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*"};
flat_buffer b{1024};
message_parser<true, string_body, fields> p;
read(is, b, p, ec);
"*")
, ec);
auto const& m = p.get();
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(! ec);
BEAST_EXPECT(p.is_done());
BEAST_EXPECT(p.is_header_done());
BEAST_EXPECT(! p.need_eof());
BEAST_EXPECT(m.method() == "GET");
BEAST_EXPECT(m.target() == "/");
BEAST_EXPECT(m.version == 11);
BEAST_EXPECT(m.fields["User-Agent"] == "test");
BEAST_EXPECT(m.body == "*");
}
#if 0
{
// test partial parsing of final chunk
// parse through the chunk body
beast::test::string_istream is{
get_io_service(), ""};
multi_buffer b;
b <<
error_code ec;
flat_buffer b;
parser_type<true> p;
p.eager(true);
ostream(b) <<
"PUT / HTTP/1.1\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"1\r\n"
"*";
error_code ec;
message_parser<true, string_body, fields> p;
read(is, b, p, ec);
BEAST_EXPECT(b.size() == 0);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(!p.is_complete());
auto used = p.put(b.data(), ec);
b.consume(used);
BEAST_EXPECT(! ec);
BEAST_EXPECT(! p.is_done());
BEAST_EXPECT(p.get().body == "*");
b << "\r\n0;d;e=3;f=\"4\"\r\n"
ostream(b) <<
"\r\n"
"0;d;e=3;f=\"4\"\r\n"
"Expires: never\r\n"
"MD5-Fingerprint: -\r\n";
// incomplete parse, missing the final crlf
BEAST_EXPECT(p.write(b.data(), ec) == 0);
used = p.put(b.data(), ec);
b.consume(used);
BEAST_EXPECT(ec == error::need_more);
ec = {};
BEAST_EXPECT(! p.is_done());
ostream(b) <<
"\r\n"; // final crlf to end message
used = p.put(b.data(), ec);
b.consume(used);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(!p.is_complete());
b << "\r\n"; // final crlf to end message
BEAST_EXPECT(p.write(b.data(), ec) == b.size());
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
}
{
error_code ec;
message_parser<false, string_body, fields> p;
std::string const s =
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 1\r\n"
"\r\n"
"*";
p.write(buffer(s), ec);
auto const& m = p.get();
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(m.status == 200);
BEAST_EXPECT(m.reason() == "OK");
BEAST_EXPECT(m.version == 11);
BEAST_EXPECT(m.fields["Server"] == "test");
BEAST_EXPECT(m.body == "*");
BEAST_EXPECT(p.is_done());
}
// skip body
{
error_code ec;
message_parser<false, string_body, fields> p;
std::string const s =
"HTTP/1.1 200 Connection Established\r\n"
"Proxy-Agent: Zscaler/5.1\r\n"
"\r\n";
p.skip_body();
p.write(buffer(s), ec);
p.skip(true);
p.put(buf(
"HTTP/1.1 200 OK\r\n"
"Content-Length: 5\r\n"
"\r\n"
"*****")
, ec);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p.is_complete());
BEAST_EXPECT(p.is_done());
BEAST_EXPECT(p.is_header_done());
BEAST_EXPECT(p.content_length() &&
*p.content_length() == 5);
}
#endif
}
void
@ -219,8 +300,8 @@ public:
read_some(ss, b, p0, ec);
b.consume(bytes_used);
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(p0.state() != parse_state::header);
BEAST_EXPECT(! p0.is_complete());
BEAST_EXPECT(p0.is_header_done());
BEAST_EXPECT(! p0.is_done());
message_parser<true,
string_body, fields> p1{std::move(p0)};
read(ss, b, p1, ec);
@ -228,12 +309,57 @@ public:
BEAST_EXPECT(p1.get().body == "*****");
}
//--------------------------------------------------------------------------
template<class DynamicBuffer>
void
testNeedMore()
{
error_code ec;
std::size_t used;
{
DynamicBuffer b;
parser_type<true> p;
ostream(b) <<
"GET / HTTP/1.1\r\n";
used = p.put(b.data(), ec);
BEAST_EXPECTS(ec == error::need_more, ec.message());
b.consume(used);
ec = {};
ostream(b) <<
"User-Agent: test\r\n"
"\r\n";
used = p.put(b.data(), ec);
BEAST_EXPECTS(! ec, ec.message());
b.consume(used);
BEAST_EXPECT(p.is_done());
BEAST_EXPECT(p.is_header_done());
}
}
void
testGotSome()
{
error_code ec;
parser_type<true> p;
auto used = p.put(buf(""), ec);
BEAST_EXPECT(ec == error::need_more);
BEAST_EXPECT(! p.got_some());
BEAST_EXPECT(used == 0);
ec = {};
used = p.put(buf("G"), ec);
BEAST_EXPECT(ec == error::need_more);
BEAST_EXPECT(p.got_some());
BEAST_EXPECT(used == 0);
}
void
run() override
{
testRead();
testParse();
testExpect100Continue();
testNeedMore<flat_buffer>();
testNeedMore<multi_buffer>();
testGotSome();
}
};

View File

@ -11,6 +11,7 @@
#include <beast/http.hpp>
#include <beast/core/consuming_buffers.hpp>
#include <beast/core/ostream.hpp>
#include <beast/core/flat_buffer.hpp>
#include <beast/core/multi_buffer.hpp>
#include <beast/unit_test/suite.hpp>
#include <boost/lexical_cast.hpp>
@ -26,7 +27,8 @@ class parser_bench_test : public beast::unit_test::suite
public:
static std::size_t constexpr N = 2000;
using corpus = std::vector<multi_buffer>;
//using corpus = std::vector<multi_buffer>;
using corpus = std::vector<flat_buffer>;
corpus creq_;
corpus cres_;
@ -41,22 +43,17 @@ public:
std::string>(buffers(bs));
}
parser_bench_test()
{
creq_ = build_corpus(N/2, std::true_type{});
cres_ = build_corpus(N/2, std::false_type{});
}
corpus
build_corpus(std::size_t n, std::true_type)
{
corpus v;
v.resize(N);
v.resize(n);
message_fuzz mg;
for(std::size_t i = 0; i < n; ++i)
{
mg.request(v[i]);
size_ += v[i].size();
BEAST_EXPECT(v[i].size() > 0);
}
return v;
}
@ -65,22 +62,23 @@ public:
build_corpus(std::size_t n, std::false_type)
{
corpus v;
v.resize(N);
v.resize(n);
message_fuzz mg;
for(std::size_t i = 0; i < n; ++i)
{
mg.response(v[i]);
size_ += v[i].size();
BEAST_EXPECT(v[i].size() > 0);
}
return v;
}
template<class ConstBufferSequence,
bool isRequest, bool isDirect, class Derived>
bool isRequest, class Derived>
static
std::size_t
feed(ConstBufferSequence const& buffers,
basic_parser<isRequest, isDirect, Derived>& parser,
basic_parser<isRequest, Derived>& parser,
error_code& ec)
{
using boost::asio::buffer_size;
@ -90,14 +88,14 @@ public:
for(;;)
{
auto const n =
parser.write(cb, ec);
parser.put(cb, ec);
if(ec)
return 0;
if(n == 0)
break;
cb.consume(n);
used += n;
if(parser.is_complete())
if(parser.is_done())
break;
if(buffer_size(cb) == 0)
break;
@ -155,14 +153,13 @@ public:
template<bool isRequest>
struct null_parser :
basic_parser<isRequest, true,
null_parser<isRequest>>
basic_parser<isRequest, null_parser<isRequest>>
{
};
template<bool isRequest, class Body, class Fields>
struct bench_parser : basic_parser<
isRequest, false, bench_parser<isRequest, Body, Fields>>
isRequest, bench_parser<isRequest, Body, Fields>>
{
using mutable_buffers_type =
boost::asio::mutable_buffers_1;
@ -194,13 +191,8 @@ public:
}
void
on_body(error_code& ec)
{
}
void
on_body(std::uint64_t content_length,
error_code& ec)
on_body(boost::optional<std::uint64_t> const&,
error_code&)
{
}
@ -217,12 +209,6 @@ public:
{
}
void
on_body(string_view const&,
error_code&)
{
}
void
on_complete(error_code&)
{
@ -235,6 +221,9 @@ public:
static std::size_t constexpr Trials = 3;
static std::size_t constexpr Repeat = 500;
creq_ = build_corpus(N/2, std::true_type{});
cres_ = build_corpus(N/2, std::false_type{});
log << "sizeof(request parser) == " <<
sizeof(null_parser<true>) << '\n';
@ -245,16 +234,6 @@ public:
((Repeat * size_ + 512) / 1024) << "KB in " <<
(Repeat * (creq_.size() + cres_.size())) << " messages";
timedTest(Trials, "nodejs_parser",
[&]
{
testParser1<nodejs_parser<
true, dynamic_body, fields>>(
Repeat, creq_);
testParser1<nodejs_parser<
false, dynamic_body, fields>>(
Repeat, cres_);
});
timedTest(Trials, "http::basic_parser",
[&]
{
@ -265,6 +244,16 @@ public:
false, dynamic_body, fields>>(
Repeat, cres_);
});
timedTest(Trials, "nodejs_parser",
[&]
{
testParser1<nodejs_parser<
true, dynamic_body, fields>>(
Repeat, creq_);
testParser1<nodejs_parser<
false, dynamic_body, fields>>(
Repeat, cres_);
});
pass();
}

View File

@ -10,11 +10,14 @@
#include "test_parser.hpp"
#include <beast/core/ostream.hpp>
#include <beast/core/static_buffer.hpp>
#include <beast/http/fields.hpp>
#include <beast/http/dynamic_body.hpp>
#include <beast/http/header_parser.hpp>
#include <beast/http/string_body.hpp>
#include <beast/test/fail_stream.hpp>
#include <beast/test/pipe_stream.hpp>
#include <beast/test/string_istream.hpp>
#include <beast/test/yield_to.hpp>
#include <beast/unit_test/suite.hpp>
@ -108,7 +111,7 @@ public:
{
multi_buffer b;
test::string_istream ss(ios_, "GET / X");
message_parser<true, dynamic_body, fields> p;
request_parser<dynamic_body> p;
read(ss, b, p);
fail();
}
@ -118,6 +121,52 @@ public:
}
}
void
testBufferOverflow()
{
{
test::pipe p{ios_};
ostream(p.server.buffer) <<
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"User-Agent: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"10\r\n"
"****************\r\n"
"0\r\n\r\n";
static_buffer_n<1024> b;
request<string_body> req;
try
{
read(p.server, b, req);
pass();
}
catch(std::exception const& e)
{
fail(e.what(), __FILE__, __LINE__);
}
}
{
test::pipe p{ios_};
ostream(p.server.buffer) <<
"GET / HTTP/1.1\r\n"
"Host: localhost\r\n"
"User-Agent: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"10\r\n"
"****************\r\n"
"0\r\n\r\n";
error_code ec;
static_buffer_n<10> b;
request<string_body> req;
read(p.server, b, req, ec);
BEAST_EXPECTS(ec == error::buffer_overflow,
ec.message());
}
}
void testFailures(yield_context do_yield)
{
char const* req[] = {
@ -318,6 +367,7 @@ public:
run() override
{
testThrow();
testBufferOverflow();
yield_to([&](yield_context yield){
testFailures(yield); });

View File

@ -16,8 +16,7 @@ namespace http {
template<bool isRequest>
class test_parser
: public basic_parser<isRequest, false,
test_parser<isRequest>>
: public basic_parser<isRequest, test_parser<isRequest>>
{
test::fail_counter* fc_ = nullptr;
@ -36,8 +35,6 @@ public:
bool got_on_header = false;
bool got_on_body = false;
bool got_content_length = false;
bool got_on_prepare = false;
bool got_on_commit = false;
bool got_on_chunk = false;
bool got_on_complete = false;
@ -50,10 +47,9 @@ public:
}
void
on_request(
string_view const& method_,
string_view const& path_,
int version_, error_code& ec)
on_request(string_view const& method_,
string_view const& path_,
int version_, error_code& ec)
{
method = std::string(
method_.data(), method_.size());
@ -98,19 +94,13 @@ public:
}
void
on_body(error_code& ec)
on_body(boost::optional<
std::uint64_t> const& content_length_,
error_code& ec)
{
got_on_body = true;
if(fc_)
fc_->fail(ec);
}
void
on_body(std::uint64_t content_length,
error_code& ec)
{
got_on_body = true;
got_content_length = true;
got_content_length =
static_cast<bool>(content_length_);
if(fc_)
fc_->fail(ec);
}
@ -120,12 +110,13 @@ public:
error_code& ec)
{
body.append(s.data(), s.size());
if(fc_)
fc_->fail(ec);
}
void
on_chunk(std::uint64_t,
string_view const&,
error_code& ec)
string_view const&, error_code& ec)
{
got_on_chunk = true;
if(fc_)

View File

@ -9,6 +9,7 @@
#include <beast/http/type_traits.hpp>
#include <beast/http/empty_body.hpp>
#include <string>
namespace beast {
namespace http {
@ -17,5 +18,7 @@ BOOST_STATIC_ASSERT(! is_body_reader<int>::value);
BOOST_STATIC_ASSERT(is_body_reader<empty_body>::value);
BOOST_STATIC_ASSERT(! is_body_writer<std::string>::value);
} // http
} // beast

View File

@ -382,7 +382,7 @@ public:
m.body = "*****";
error_code ec;
write(fs, m, ec);
if(ec == boost::asio::error::eof)
if(ec == error::end_of_stream)
{
BEAST_EXPECT(fs.next_layer().str ==
"GET / HTTP/1.0\r\n"
@ -415,7 +415,7 @@ public:
m.body = "*****";
error_code ec;
async_write(fs, m, do_yield[ec]);
if(ec == boost::asio::error::eof)
if(ec == error::end_of_stream)
{
BEAST_EXPECT(fs.next_layer().str ==
"GET / HTTP/1.0\r\n"
@ -559,7 +559,7 @@ public:
test::string_ostream ss(ios_);
error_code ec;
write(ss, m, ec);
BEAST_EXPECT(ec == boost::asio::error::eof);
BEAST_EXPECT(ec == error::end_of_stream);
BEAST_EXPECT(ss.str ==
"GET / HTTP/1.0\r\n"
"User-Agent: test\r\n"
@ -596,7 +596,7 @@ public:
test::string_ostream ss(ios_);
error_code ec;
write(ss, m, ec);
BEAST_EXPECT(ec == boost::asio::error::eof);
BEAST_EXPECT(ec == error::end_of_stream);
BEAST_EXPECT(ss.str ==
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
@ -949,93 +949,6 @@ public:
}
}
/** Execute a child process and return the output as an HTTP response.
@param input A stream to read the child process output from.
@param output A stream to write the HTTP response to.
*/
template<class SyncReadStream, class SyncWriteStream>
void
cgi_process(SyncReadStream& input, SyncWriteStream& output, error_code& ec)
{
multi_buffer b;
message<false, buffer_body<true,
typename multi_buffer::const_buffers_type>, fields> m;
m.status = 200;
m.version = 11;
m.fields.insert("Server", "cgi-process");
m.fields.insert("Transfer-Encoding", "chunked");
m.body.first = boost::none;
m.body.second = true;
auto sr = make_serializer(m);
// send the header first, so the
// other end gets it right away
for(;;)
{
write_some(output, sr, ec);
if(ec == error::need_more)
{
ec = {};
break;
}
if(ec)
return;
}
// send the body
for(;;)
{
// read from input
auto bytes_transferred =
input.read_some(b.prepare(1024), ec);
if(ec == boost::asio::error::eof)
{
BOOST_ASSERT(bytes_transferred == 0);
ec = {};
m.body = {boost::none, false};
}
else
{
if(ec)
return;
b.commit(bytes_transferred);
m.body = {b.data(), true};
}
// write to output
for(;;)
{
write_some(output, sr, ec);
if(ec == error::need_more)
{
ec = {};
break;
}
if(ec)
return;
if(sr.is_done())
goto is_done;
}
b.consume(b.size());
}
is_done:
;
}
void
testCgiRelay()
{
error_code ec;
std::string const body = "Hello, world!\n";
test::string_ostream so{get_io_service(), 3};
test::string_istream si{get_io_service(), body, 6};
cgi_process(si, so, ec);
BEAST_EXPECT(equal_body<false>(so.str, body));
}
void run() override
{
yield_to([&](yield_context yield){
@ -1046,7 +959,6 @@ public:
test_std_ostream();
testOstream();
testIoService();
testCgiRelay();
yield_to(
[&](yield_context yield)
{