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 request-method
* Refactor treatment of status code and obsolete reason * Refactor treatment of status code and obsolete reason
* Refactor HTTP serialization and parsing
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View File

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

View File

@ -59,8 +59,10 @@
<bridgehead renderas="sect3">Functions</bridgehead> <bridgehead renderas="sect3">Functions</bridgehead>
<simplelist type="vert" columns="1"> <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">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_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">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__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_keep_alive">is_keep_alive</link></member>
<member><link linkend="beast.ref.http__is_upgrade">is_upgrade</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__operator_ls_">operator&lt;&lt;</link></member>
<member><link linkend="beast.ref.http__prepare">prepare</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">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__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__string_to_verb">string_to_verb</link></member>
<member><link linkend="beast.ref.http__swap">swap</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__to_string">to_string</link></member>
<member><link linkend="beast.ref.http__write">write</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> <member><link linkend="beast.ref.http__write_some">write_some</link></member>
</simplelist> </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>
<entry valign="top"> <entry valign="top">
<bridgehead renderas="sect3">Constants</bridgehead> <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__status">status</link></member>
<member><link linkend="beast.ref.http__verb">verb</link></member> <member><link linkend="beast.ref.http__verb">verb</link></member>
</simplelist> </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> <bridgehead renderas="sect3">Concepts</bridgehead>
<simplelist type="vert" columns="1"> <simplelist type="vert" columns="1">
<member><link linkend="beast.ref.Body">Body</link></member> <member><link linkend="beast.ref.Body">Body</link></member>

View File

@ -15,7 +15,7 @@
int main() int main()
{ {
// Normal boost::asio setup // Normal boost::asio setup
std::string const host = "boost.org"; std::string const host = "www.example.com";
boost::asio::io_service ios; boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios}; boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios}; boost::asio::ip::tcp::socket sock{ios};
@ -37,5 +37,5 @@ int main()
beast::flat_buffer b; beast::flat_buffer b;
beast::http::response<beast::http::dynamic_body> res; beast::http::response<beast::http::dynamic_body> res;
beast::http::read(sock, b, 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; buffer_type b;
std::condition_variable cv; std::condition_variable cv;
std::unique_ptr<read_op> op; std::unique_ptr<read_op> op;
bool eof = false;
}; };
state s_[2]; state s_[2];
@ -151,6 +152,15 @@ public:
std::size_t>::max)()); 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> template<class MutableBufferSequence>
std::size_t std::size_t
read_some(MutableBufferSequence const& buffers); read_some(MutableBufferSequence const& buffers);
@ -275,22 +285,49 @@ read_op_impl<Handler, Buffers>::operator()()
{ {
BOOST_ASSERT(s_.in_.op); BOOST_ASSERT(s_.in_.op);
std::unique_lock<std::mutex> lock{s_.in_.m}; std::unique_lock<std::mutex> lock{s_.in_.m};
BOOST_ASSERT(buffer_size(s_.in_.b.data()) > 0); if(s_.in_.b.size() > 0)
auto const bytes_transferred = buffer_copy( {
b_, s_.in_.b.data(), s_.read_max_); auto const bytes_transferred = buffer_copy(
s_.in_.b.consume(bytes_transferred); b_, s_.in_.b.data(), s_.read_max_);
auto& s = s_; s_.in_.b.consume(bytes_transferred);
Handler h{std::move(h_)}; auto& s = s_;
lock.unlock(); Handler h{std::move(h_)};
s.in_.op.reset(nullptr); lock.unlock();
++s.nread; s.in_.op.reset(nullptr);
s.ios_.post(bind_handler(std::move(h), ++s.nread;
error_code{}, bytes_transferred)); 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> template<class MutableBufferSequence>
std::size_t std::size_t
pipe::stream:: pipe::stream::
@ -318,19 +355,28 @@ read_some(MutableBufferSequence const& buffers,
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
using boost::asio::buffer_size; using boost::asio::buffer_size;
BOOST_ASSERT(! in_.op); BOOST_ASSERT(! in_.op);
BOOST_ASSERT(buffer_size(buffers) > 0);
if(fc_ && fc_->fail(ec)) if(fc_ && fc_->fail(ec))
return 0; return 0;
std::unique_lock<std::mutex> lock{in_.m}; std::unique_lock<std::mutex> lock{in_.m};
in_.cv.wait(lock, in_.cv.wait(lock,
[&]() [&]()
{ {
return return in_.b.size() > 0 || in_.eof;
buffer_size(buffers) == 0 ||
buffer_size(in_.b.data()) > 0;
}); });
auto const bytes_transferred = buffer_copy( std::size_t bytes_transferred;
buffers, in_.b.data(), write_max_); if(in_.b.size() > 0)
in_.b.consume(bytes_transferred); {
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; ++nread;
return bytes_transferred; return bytes_transferred;
} }
@ -347,9 +393,10 @@ async_read_some(MutableBufferSequence const& buffers,
"MutableBufferSequence requirements not met"); "MutableBufferSequence requirements not met");
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
using boost::asio::buffer_size; using boost::asio::buffer_size;
BOOST_ASSERT(! in_.op);
BOOST_ASSERT(buffer_size(buffers) > 0);
async_completion<ReadHandler, async_completion<ReadHandler,
void(error_code, std::size_t)> init{handler}; void(error_code, std::size_t)> init{handler};
BOOST_ASSERT(! in_.op);
if(fc_) if(fc_)
{ {
error_code ec; error_code ec;
@ -359,7 +406,14 @@ async_read_some(MutableBufferSequence const& buffers,
} }
{ {
std::unique_lock<std::mutex> lock{in_.m}; 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) buffer_size(in_.b.data()) > 0)
{ {
auto const bytes_transferred = buffer_copy( auto const bytes_transferred = buffer_copy(
@ -389,6 +443,7 @@ write_some(ConstBufferSequence const& buffers)
static_assert(is_const_buffer_sequence< static_assert(is_const_buffer_sequence<
ConstBufferSequence>::value, ConstBufferSequence>::value,
"ConstBufferSequence requirements not met"); "ConstBufferSequence requirements not met");
BOOST_ASSERT(! out_.eof);
error_code ec; error_code ec;
auto const bytes_transferred = auto const bytes_transferred =
write_some(buffers, ec); write_some(buffers, ec);
@ -408,6 +463,7 @@ write_some(
"ConstBufferSequence requirements not met"); "ConstBufferSequence requirements not met");
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
using boost::asio::buffer_size; using boost::asio::buffer_size;
BOOST_ASSERT(! out_.eof);
if(fc_ && fc_->fail(ec)) if(fc_ && fc_->fail(ec))
return 0; return 0;
auto const n = (std::min)( auto const n = (std::min)(
@ -437,6 +493,7 @@ async_write_some(ConstBufferSequence const& buffers,
"ConstBufferSequence requirements not met"); "ConstBufferSequence requirements not met");
using boost::asio::buffer_copy; using boost::asio::buffer_copy;
using boost::asio::buffer_size; using boost::asio::buffer_size;
BOOST_ASSERT(! out_.eof);
async_completion<WriteHandler, async_completion<WriteHandler,
void(error_code, std::size_t)> init{handler}; void(error_code, std::size_t)> init{handler};
if(fc_) if(fc_)

View File

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

View File

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

View File

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

View File

@ -21,41 +21,6 @@
namespace beast { namespace beast {
namespace http { 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. /** A parser for decoding HTTP/1 wire format messages.
This parser is designed to efficiently parse messages in the This parser is designed to efficiently parse messages in the
@ -83,18 +48,13 @@ enum class parse_state
struct derived struct derived
: basic_parser<isRequest, derived<isRequest>> : 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 // When isRequest == true, called
// after the Request Line is received. // after the Request Line is received.
// //
void void
on_request( on_request(
string_view const& method, string_view method,
string_view const& target, string_view target,
int version, int version,
error_code& ec); error_code& ec);
@ -104,7 +64,7 @@ enum class parse_state
void void
on_response( on_response(
int status, int status,
string_view const& reason, string_view reason,
int version, int version,
error_code& ec); error_code& ec);
@ -112,8 +72,8 @@ enum class parse_state
// //
void void
on_field( on_field(
string_view const& name, string_view name,
string_view const& value, string_view value,
error_code& ec); error_code& ec);
// Called after the header is complete. // Called after the header is complete.
@ -123,40 +83,19 @@ enum class parse_state
error_code& ec); error_code& ec);
// Called once before the body, if any, is started. // 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 void
on_body(); on_body(
boost::optional<std::uint64_t> content_length,
error_code& ec);
// Called zero or more times to provide body data. // Called zero or more times to provide body data.
// //
// Only used if isDirect == false
//
void void
on_data( on_data(
string_view const& s, string_view s,
error_code& ec); 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 // If the Transfer-Encoding is specified, and the
// last item in the list of encodings is "chunked", // last item in the list of encodings is "chunked",
// called after receiving a chunk header or a final // called after receiving a chunk header or a final
@ -164,8 +103,8 @@ enum class parse_state
// //
void void
on_chunk( on_chunk(
std::uint64_t length, // Length of this chunk std::uint64_t length, // Length of this chunk
string_view const& ext, // The chunk extensions, if any string_view const& ext, // The chunk extensions, if any
error_code& ec); error_code& ec);
// Called once when the message is complete. // Called once when the message is complete.
@ -177,16 +116,7 @@ enum class parse_state
@endcode @endcode
If a callback sets the error code, the error will be propagated If a callback sets the error code, the error will be propagated
to the caller of the parser. Behavior of parsing after an error to the caller of the parser.
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.
The parser is optimized for the case where the input buffer The parser is optimized for the case where the input buffer
sequence consists of a single contiguous buffer. The 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 @tparam isRequest A `bool` indicating whether the parser will be
presented with request or response message. 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 @tparam Derived The derived class type. This is part of the
Curiously Recurring Template Pattern interface. Curiously Recurring Template Pattern interface.
*/ */
template<bool isRequest, bool isDirect, class Derived> template<bool isRequest, class Derived>
class basic_parser class basic_parser
: private detail::basic_parser_base : private detail::basic_parser_base
{ {
template<bool OtherIsRequest, template<bool OtherIsRequest, class OtherDerived>
bool OtherIsDirect, class OtherDerived>
friend class basic_parser; friend class basic_parser;
// Message will be complete after reading header // Message will be complete after reading header
static unsigned constexpr flagSkipBody = 1<< 0; static unsigned constexpr flagSkipBody = 1<< 0;
// Consume input buffers across semantic boundaries
static unsigned constexpr flagEager = 1<< 1;
static unsigned constexpr flagOnBody = 1<< 1;
// The parser has read at least one byte // The parser has read at least one byte
static unsigned constexpr flagGotSome = 1<< 2; static unsigned constexpr flagGotSome = 1<< 2;
@ -248,10 +173,8 @@ class basic_parser
std::size_t buf_len_ = 0; std::size_t buf_len_ = 0;
std::size_t skip_ = 0; // search from here std::size_t skip_ = 0; // search from here
std::size_t x_; // scratch variable std::size_t x_; // scratch variable
state state_ = state::nothing_yet;
unsigned f_ = 0; // flags unsigned f_ = 0; // flags
parse_state state_ = parse_state::header;
string_view ext_;
string_view body_;
public: public:
/// Copy constructor (disallowed) /// Copy constructor (disallowed)
@ -264,7 +187,8 @@ public:
basic_parser() = default; basic_parser() = default;
/// `true` if this parser parses requests, `false` for responses. /// `true` if this parser parses requests, `false` for responses.
static bool constexpr is_request = isRequest; using is_request =
std::integral_constant<bool, isRequest>;
/// Destructor /// Destructor
~basic_parser() = default; ~basic_parser() = default;
@ -274,83 +198,70 @@ public:
After the move, the only valid operation on the After the move, the only valid operation on the
moved-from object is destruction. moved-from object is destruction.
*/ */
template<bool OtherIsDirect, class OtherDerived> template<class OtherDerived>
basic_parser(basic_parser< basic_parser(basic_parser<isRequest, OtherDerived>&&);
isRequest, OtherIsDirect, 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 This is used to pass a derived class where a base class is
see an HTTP body, regardless of the presence or absence of expected, to choose a correct function overload when the
certain fields such as Content-Length. resolution would be ambiguous.
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 basic_parser&
skip_body(); base()
/** 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
{ {
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. /// Returns `true` if the parser has received at least one byte of input.
bool bool
got_some() const got_some() const
{ {
return (f_ & flagGotSome) != 0; return state_ != state::nothing_yet;
}
/// 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;
} }
/** Returns `true` if the message is complete. /** Returns `true` if the message is complete.
The message is complete after a full header is The message is complete after the full header is prduced
parsed and one of the following is true: 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 there is no body.
@li The semantics of the message indicate a body is @li The semantics of the message indicate a body is expected,
expected, and the entire body was received. and the entire body was parsed.
*/ */
bool 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. /** 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 bool
is_upgrade() const is_upgrade() const
@ -358,16 +269,10 @@ public:
return (f_ & flagConnectionUpgrade) != 0; 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. @note The return value is undefined unless
*/ @ref is_header_done would return `true`.
bool
is_keep_alive() const;
/** Returns `true` if the chunked Transfer-Encoding is specified.
@note Only valid after parsing a complete header.
*/ */
bool bool
is_chunked() const is_chunked() const
@ -375,34 +280,125 @@ public:
return (f_ & flagChunked) != 0; 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 @note The return value is undefined unless
stored in the caller provided buffers. Upon success, @ref is_header_done would return `true`.
a positive return value indicates that the parser */
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 made forward progress, consuming that number of
bytes. bytes.
A return value of zero indicates that the parser In some cases there may be an insufficient number of octets
requires additional input. In this case the caller in the input buffer in order to make forward progress. This
should append additional bytes to the input buffer is indicated by the the code @ref error::need_more. When
sequence and call @ref write again. 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 @param buffers An object meeting the requirements of
@b ConstBufferSequence that represents the message. @b ConstBufferSequence that represents the message.
@param ec Set to the error, if any occurred. @param ec Set to the error, if any occurred.
@return The number of bytes consumed in the buffer @return The number of octets consumed in the buffer
sequence. sequence. The caller should remove these octets even if the
error is set.
*/ */
template<class ConstBufferSequence> template<class ConstBufferSequence>
std::size_t std::size_t
write(ConstBufferSequence const& buffers, error_code& ec); put(ConstBufferSequence const& buffers, error_code& ec);
#if ! BEAST_DOXYGEN #if ! BEAST_DOXYGEN
std::size_t std::size_t
write(boost::asio::const_buffers_1 const& buffer, put(boost::asio::const_buffers_1 const& buffer,
error_code& ec); error_code& ec);
#endif #endif
@ -423,139 +419,7 @@ public:
@param ec Set to the error, if any occurred. @param ec Set to the error, if any occurred.
*/ */
void void
write_eof(error_code& ec); put_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;
}
}
private: private:
inline inline
@ -570,68 +434,42 @@ private:
maybe_flatten( maybe_flatten(
ConstBufferSequence const& buffers); ConstBufferSequence const& buffers);
std::size_t void
do_write(boost::asio::const_buffers_1 const& buffer, 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); error_code& ec, std::true_type);
std::size_t void
do_write(boost::asio::const_buffers_1 const& buffer, parse_header(char const*& p, char const* term,
error_code& ec, std::false_type); error_code& ec, std::false_type);
void void
parse_startline(char const*& it, parse_body(char const*& p,
int& version, int& status, std::size_t n, error_code& ec);
error_code& ec, std::true_type);
void void
parse_startline(char const*& it, parse_body_to_eof(char const*& p,
int& version, int& status, std::size_t n, error_code& ec);
error_code& ec, std::false_type);
void 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); char const* last, error_code& ec);
void void
do_field( do_field(string_view const& name,
string_view const& name, string_view const& value,
string_view const& value, error_code& ec);
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);
}; };
} // http } // http

View File

@ -8,9 +8,9 @@
#ifndef BEAST_HTTP_BUFFER_BODY_HPP #ifndef BEAST_HTTP_BUFFER_BODY_HPP
#define 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/error.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/type_traits.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@ -31,41 +31,30 @@ namespace http {
... ...
} }
@endcode @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 struct buffer_body
{ {
static_assert(is_const_buffer_sequence<ConstBufferSequence>::value, /// The type of the body member when used in a message.
"ConstBufferSequence requirements not met"); 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 number of octets in the buffer pointed to by @ref data
the current buffer sequence to be written.
The second element of the pair indicates whether or not If @ref data is `nullptr` during serialization, this value
additional buffers will be available. A value of `false` is not inspected.
indicates the end of the message body. */
std::size_t size;
If the buffer in the value is disengaged, and the second /// `true` if this is not the last buffer.
element of the pair is `true`, @ref serializer operations bool more;
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>;
#if BEAST_DOXYGEN #if BEAST_DOXYGEN
/// The algorithm to obtain buffers representing the body /// The algorithm to obtain buffers representing the body
@ -77,10 +66,10 @@ struct buffer_body
value_type const& body_; value_type const& body_;
public: public:
using is_deferred = using is_deferred = std::false_type;
std::integral_constant<bool, isDeferred>;
using const_buffers_type = ConstBufferSequence; using const_buffers_type =
boost::asio::const_buffers_1;
template<bool isRequest, class Fields> template<bool isRequest, class Fields>
explicit 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) get(error_code& ec)
{ {
if(toggle_) if(toggle_)
{ {
if(body_.second) if(body_.more)
{ {
toggle_ = false; toggle_ = false;
ec = error::need_more; ec = error::need_buffer;
} }
return boost::none; return boost::none;
} }
if(body_.first) if(body_.data)
{ {
toggle_ = true; toggle_ = true;
return {{*body_.first, body_.second}}; return {{const_buffers_type{
body_.data, body_.size}, body_.more}};
} }
if(body_.second) if(body_.more)
ec = error::need_more; ec = error::need_buffer;
return boost::none; return boost::none;
} }
}; };
#endif #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 #if ! BEAST_DOXYGEN
// operator<< is not supported for buffer_body"
template<bool isRequest, class Fields, template<bool isRequest, class Fields,
bool isDeferred, class ConstBufferSequence> bool isDeferred, class ConstBufferSequence>
std::ostream& std::ostream&
operator<<(std::ostream& os, message< operator<<(std::ostream& os, message<isRequest,
isRequest, buffer_body<isDeferred, buffer_body, Fields> const& msg) = delete;
ConstBufferSequence>, Fields> const& msg)
{
static_assert(sizeof(ConstBufferSequence) == -1,
"operator<< is not supported for buffer_body");
}
#endif #endif
} // http } // http

View File

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

View File

@ -9,8 +9,8 @@
#define BEAST_HTTP_DYNAMIC_BODY_HPP #define BEAST_HTTP_DYNAMIC_BODY_HPP
#include <beast/config.hpp> #include <beast/config.hpp>
#include <beast/core/error.hpp>
#include <beast/core/multi_buffer.hpp> #include <beast/core/multi_buffer.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <utility> #include <utility>
@ -28,57 +28,6 @@ struct basic_dynamic_body
/// The type of the body member when used in a message. /// The type of the body member when used in a message.
using value_type = DynamicBuffer; 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 #if BEAST_DOXYGEN
/// The algorithm to obtain buffers representing the body /// The algorithm to obtain buffers representing the body
using reader = implementation_defined; using reader = implementation_defined;
@ -119,6 +68,58 @@ struct basic_dynamic_body
} }
}; };
#endif #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 /** A dynamic message body represented by a @ref multi_buffer

View File

@ -9,6 +9,7 @@
#define BEAST_HTTP_EMPTY_BODY_HPP #define BEAST_HTTP_EMPTY_BODY_HPP
#include <beast/config.hpp> #include <beast/config.hpp>
#include <beast/http/error.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
@ -26,6 +27,8 @@ struct empty_body
/// The type of the body member when used in a message. /// The type of the body member when used in a message.
struct value_type struct value_type
{ {
// VFALCO We could stash boost::optional<std::uint64_t>
// for the content length here, set on init()
}; };
#if BEAST_DOXYGEN #if BEAST_DOXYGEN
@ -64,6 +67,39 @@ struct empty_body
} }
}; };
#endif #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 } // http

View File

@ -14,36 +14,64 @@
namespace beast { namespace beast {
namespace http { namespace http {
/// Error codes returned from HTTP parsing /// Error codes returned from HTTP algorithms and operations.
enum class error enum class error
{ {
/** The end of the stream was reached. /** The end of the stream was reached.
This error is returned by @ref basic_parser::write_eof This error is returned under the following conditions:
when the end of stream is reached and there are no
unparsed bytes in the stream buffer. @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, end_of_stream = 1,
/** The incoming message is incomplete. /** The incoming message is incomplete.
This happens when the end of stream is reached This happens when the end of stream is reached during
and some bytes have been received, but not the parsing and some octets have been received, but not the
entire message. entire message.
*/ */
partial_message, partial_message,
/** Additional buffers are required. /** Additional buffers are required.
This error is generated during serialization of HTTP This error is returned during parsing when additional
messages using the @ref buffer_body representation. octets are needed. The caller should append more data
It indicates to the caller that an additional buffer to the existing buffer and retry the parse operaetion.
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.
*/ */
need_more, 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. /** Buffer maximum exceeded.
This error is returned when reading HTTP content This error is returned when reading HTTP content
@ -52,6 +80,10 @@ enum class error
*/ */
buffer_overflow, buffer_overflow,
//
// (parser errors)
//
/// The line ending was malformed /// The line ending was malformed
bad_line_ending, bad_line_ending,

View File

@ -33,18 +33,19 @@ namespace http {
*/ */
template<bool isRequest, class Fields> template<bool isRequest, class Fields>
class header_parser class header_parser
: public basic_parser<isRequest, false, : public basic_parser<isRequest,
header_parser<isRequest, Fields>> header_parser<isRequest, Fields>>
{ {
header<isRequest, Fields> h_; header<isRequest, Fields> h_;
string_view body_;
public: public:
using mutable_buffers_type =
boost::asio::null_buffers;
/// The type of @ref header this object produces. /// The type of @ref header this object produces.
using value_type = header<isRequest, Fields>; using value_type = header<isRequest, Fields>;
/// Default constructor.
header_parser() = default;
/// Copy constructor. /// Copy constructor.
header_parser(header_parser const&) = default; header_parser(header_parser const&) = default;
@ -58,18 +59,49 @@ public:
*/ */
header_parser(header_parser&&) = default; 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 /** Constructor
@param args If present, additional arguments to be @param args Optional arguments forwarded
forwarded to the @ref beast::http::header constructor. forwarded to the @ref http::header constructor.
*/ */
#if BEAST_DOXYGEN
template<class... Args> template<class... Args>
explicit explicit
header_parser(Args&&... args); 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 /** 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& value_type const&
get() const get() const
@ -79,7 +111,8 @@ public:
/** Returns the parsed header. /** 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& value_type&
get() get()
@ -89,8 +122,10 @@ public:
/** Returns ownership of the parsed header. /** Returns ownership of the parsed header.
Ownership is transferred to the caller. Only Ownership is transferred to the caller.
valid if @ref got_header would return `true`.
@note The return value is undefined unless
@ref is_header_done would return `true`.
Requires: Requires:
@ref value_type is @b MoveConstructible @ref value_type is @b MoveConstructible
@ -98,20 +133,18 @@ public:
value_type value_type
release() release()
{ {
static_assert(std::is_move_constructible<decltype(h_)>::value, static_assert(
std::is_move_constructible<decltype(h_)>::value,
"MoveConstructible requirements not met"); "MoveConstructible requirements not met");
return std::move(h_); return std::move(h_);
} }
private: private:
friend class basic_parser< friend class basic_parser<isRequest, header_parser>;
isRequest, false, header_parser>;
void void
on_request( on_request(string_view method,
string_view const& method, string_view path, int version, error_code&)
string_view const& path,
int version, error_code&)
{ {
h_.target(path); h_.target(path);
h_.method(method); h_.method(method);
@ -119,9 +152,8 @@ private:
} }
void void
on_response(int status, on_response(int status, string_view reason,
string_view const& reason, int version, error_code&)
int version, error_code&)
{ {
h_.status = status; h_.status = status;
h_.version = version; h_.version = version;
@ -129,9 +161,8 @@ private:
} }
void void
on_field(string_view const& name, on_field(string_view name,
string_view const& value, string_view value, error_code&)
error_code&)
{ {
h_.fields.insert(name, value); h_.fields.insert(name, value);
} }
@ -142,41 +173,28 @@ private:
} }
void void
on_body(error_code& ec) on_body(boost::optional<std::
uint64_t> const&, error_code&)
{ {
} }
void void
on_body(std::uint64_t content_length, on_data(string_view s, error_code&)
error_code& ec)
{ {
body_ = s;
} }
void void
on_data(string_view const& s, on_chunk(std::uint64_t,
error_code& ec) string_view const&, error_code&)
{
}
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)
{ {
body_ = {};
} }
void void
on_complete(error_code&) 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 { namespace http {
template<bool isRequest, class Fields> template<bool isRequest, class Fields>
template<class... Args> template<class Arg0, class... ArgN, class>
header_parser<isRequest, Fields>:: header_parser<isRequest, Fields>::
header_parser(Args&&... args) header_parser(Arg0&& arg0, ArgN&&... argn)
: h_(std::forward<Args>(args)...) : h_(std::forward<Arg0>(arg0),
std::forward<ArgN>(argn)...)
{ {
} }

View File

@ -26,8 +26,7 @@ message_parser<isRequest, Body, Fields>::
message_parser(header_parser< message_parser(header_parser<
isRequest, Fields>&& parser, Args&&... args) isRequest, Fields>&& parser, Args&&... args)
: base_type(std::move(static_cast<basic_parser< : base_type(std::move(static_cast<basic_parser<
isRequest, false, header_parser< isRequest, header_parser<isRequest, Fields>>&>(parser)))
isRequest, Fields>>&>(parser)))
, m_(parser.release(), std::forward<Args>(args)...) , m_(parser.release(), std::forward<Args>(args)...)
{ {
} }

View File

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

View File

@ -81,19 +81,7 @@ serializer(message<isRequest, Body, Fields> const& m,
: m_(m) : m_(m)
, d_(d) , d_(d)
, b_(1024, alloc) , 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, template<bool isRequest, class Body, class Fields,
@ -107,6 +95,22 @@ get(error_code& ec, Visit&& visit)
using boost::asio::buffer_size; using boost::asio::buffer_size;
switch(s_) 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: case do_init:
{ {
if(split_) if(split_)
@ -171,6 +175,8 @@ get(error_code& ec, Visit&& visit)
//---------------------------------------------------------------------- //----------------------------------------------------------------------
go_init_c:
s_ = do_init_c;
case do_init_c: case do_init_c:
{ {
if(split_) if(split_)
@ -320,7 +326,7 @@ consume(std::size_t n)
break; break;
// VFALCO delete b_? // VFALCO delete b_?
header_done_ = true; header_done_ = true;
if(! is_deferred::value) if(! split_)
goto go_complete; goto go_complete;
s_ = do_body; s_ = do_body;
break; break;
@ -364,7 +370,7 @@ consume(std::size_t n)
break; break;
// VFALCO delete b_? // VFALCO delete b_?
header_done_ = true; header_done_ = true;
if(! is_deferred::value) if(! split_)
{ {
s_ = do_final_c; s_ = do_final_c;
break; break;

View File

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

View File

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

View File

@ -66,12 +66,12 @@ namespace http {
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, bool isDirect, class Derived> bool isRequest, class Derived>
std::size_t std::size_t
read_some( read_some(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser); basic_parser<isRequest, Derived>& parser);
/** Read some HTTP/1 message data from a stream. /** Read some HTTP/1 message data from a stream.
@ -122,12 +122,12 @@ read_some(
template< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, bool isDirect, class Derived> bool isRequest, class Derived>
std::size_t std::size_t
read_some( read_some(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser, basic_parser<isRequest, Derived>& parser,
error_code& ec); error_code& ec);
/** Start an asynchronous operation to read some HTTP/1 message data from a stream. /** Start an asynchronous operation to read some HTTP/1 message data from a stream.
@ -192,7 +192,7 @@ read_some(
template< template<
class AsyncReadStream, class AsyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, bool isDirect, class Derived, bool isRequest, class Derived,
class ReadHandler> class ReadHandler>
#if BEAST_DOXYGEN #if BEAST_DOXYGEN
void_or_deduced void_or_deduced
@ -203,7 +203,170 @@ async_return_type<
async_read_some( async_read_some(
AsyncReadStream& stream, AsyncReadStream& stream,
DynamicBuffer& buffer, 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); ReadHandler&& handler);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -241,16 +404,19 @@ async_read_some(
@param parser The parser to use. @param parser The parser to use.
@throws system_error Thrown on failure. @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< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, bool isDirect, class Derived> bool isRequest, class Derived>
void void
read( read(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser); basic_parser<isRequest, Derived>& parser);
/** Read into an HTTP/1 parser from a stream. /** Read into an HTTP/1 parser from a stream.
@ -285,16 +451,19 @@ read(
@param parser The parser to use. @param parser The parser to use.
@param ec Set to the error, if any occurred. @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< template<
class SyncReadStream, class SyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, bool isDirect, class Derived> bool isRequest, class Derived>
void void
read( read(
SyncReadStream& stream, SyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser, basic_parser<isRequest, Derived>& parser,
error_code& ec); error_code& ec);
/** Read into an HTTP/1 parser asynchronously from a stream. /** 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 immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`. 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< template<
class AsyncReadStream, class AsyncReadStream,
class DynamicBuffer, class DynamicBuffer,
bool isRequest, bool isDirect, class Derived, bool isRequest, class Derived,
class ReadHandler> class ReadHandler>
#if BEAST_DOXYGEN #if BEAST_DOXYGEN
void_or_deduced void_or_deduced
@ -357,9 +529,11 @@ async_return_type<
async_read( async_read(
AsyncReadStream& stream, AsyncReadStream& stream,
DynamicBuffer& buffer, DynamicBuffer& buffer,
basic_parser<isRequest, isDirect, Derived>& parser, basic_parser<isRequest, Derived>& parser,
ReadHandler&& handler); ReadHandler&& handler);
//------------------------------------------------------------------------------
/** Read an HTTP/1 message from a stream. /** Read an HTTP/1 message from a stream.
This function is used to synchronously read a message from This function is used to synchronously read a message from
@ -522,7 +696,6 @@ async_read(
} // http } // http
} // beast } // beast
#include <beast/http/impl/async_read.ipp>
#include <beast/http/impl/read.ipp> #include <beast/http/impl/read.ipp>
#endif #endif

View File

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

View File

@ -9,7 +9,7 @@
#define BEAST_HTTP_STRING_BODY_HPP #define BEAST_HTTP_STRING_BODY_HPP
#include <beast/config.hpp> #include <beast/config.hpp>
#include <beast/core/error.hpp> #include <beast/http/error.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/core/detail/type_traits.hpp> #include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
@ -30,68 +30,6 @@ struct string_body
/// The type of the body member when used in a message. /// The type of the body member when used in a message.
using value_type = std::string; 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 #if BEAST_DOXYGEN
/// The algorithm to obtain buffers representing the body /// The algorithm to obtain buffers representing the body
using reader = implementation_defined; using reader = implementation_defined;
@ -134,6 +72,69 @@ struct string_body
} }
}; };
#endif #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 } // http

View File

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

View File

@ -28,27 +28,35 @@
namespace beast { namespace beast {
namespace http { 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 This function is used to write part of a message to a stream using
stream. The function call will block until one of the following a caller-provided HTTP/1 serializer. The call will block until one
conditions is true: of the following conditions is true:
@li One or more bytes have been transferred. @li One or more bytes have been transferred.
@li The function @ref serializer::is_done returns `true`
@li An error occurs on the stream. @li An error occurs on the stream.
In order to completely serialize a message, this function This operation is implemented in terms of one or more calls
should be called until `sr.is_done()` returns `true`. to the stream's `write_some` function.
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 write to. This type must @param stream The stream to which the data is to be written.
satisfy the requirements of @b SyncWriteStream. The type must support the @b SyncWriteStream concept.
@param sr The serializer to use. @param sr The serializer to use.
@throws system_error Thrown on failure. @throws system_error Thrown on failure.
@see @ref async_write_some, @ref serializer @see serializer
*/ */
template<class SyncWriteStream, template<class SyncWriteStream,
bool isRequest, class Body, class Fields, bool isRequest, class Body, class Fields,
@ -57,22 +65,29 @@ void
write_some(SyncWriteStream& stream, serializer< write_some(SyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr); 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 part of a message to a stream using
a caller-provided HTTP/1 serializer. The call will block until one
This function is used to write serialized message data to the of the following conditions is true:
stream. The function call will block until one of the following
conditions is true:
@li One or more bytes have been transferred. @li One or more bytes have been transferred.
@li The function @ref serializer::is_done returns `true`
@li An error occurs on the stream. @li An error occurs on the stream.
In order to completely serialize a message, this function This operation is implemented in terms of one or more calls
should be called until `sr.is_done()` returns `true`. to the stream's `write_some` function.
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 write to. This type must @param stream The stream to which the data is to be written.
satisfy the requirements of @b SyncWriteStream. The type must support the @b SyncWriteStream concept.
@param sr The serializer to use. @param sr The serializer to use.
@ -88,37 +103,47 @@ write_some(SyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr, isRequest, Body, Fields, Decorator, Allocator>& sr,
error_code& ec); 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 This function is used to write part of a message to a stream
message data to the stream. The function call always returns asynchronously using a caller-provided HTTP/1 serializer. The function
immediately. The asynchronous operation will continue until call always returns immediately. The asynchronous operation will continue
one of the following conditions is true: until one of the following conditions is true:
@li One or more bytes have been transferred. @li One or more bytes have been transferred.
@li The function @ref serializer::is_done returns `true`
@li An error occurs on the stream. @li An error occurs on the stream.
In order to completely serialize a message, this function This operation is implemented in terms of zero or more calls to the stream's
should be called until `sr.is_done()` returns `true`. `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 The amount of data actually transferred is controlled by the behavior
satisfy the requirements of @b SyncWriteStream. 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 AsyncWriteStream concept.
@param sr The serializer to use for writing. @param sr The serializer to use.
@param handler The handler to be called when the request @param handler The handler to be called when the operation
completes. Copies will be made of the handler as required. The completes. Copies will be made of the handler as required.
equivalent function signature of the handler must be: The equivalent function signature of the handler must be:
@code void handler( @code void handler(
error_code const& ec // Result of operation error_code const& error // result of operation
); @endcode ); @endcode
Regardless of whether the asynchronous operation completes Regardless of whether the asynchronous operation completes
immediately or not, the handler will not be invoked from within immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`. manner equivalent to using `boost::asio::io_service::post`.
@see @ref write_some, @ref serializer @see @ref serializer
*/ */
template<class AsyncWriteStream, template<class AsyncWriteStream,
bool isRequest, class Body, class Fields, bool isRequest, class Body, class Fields,
@ -132,98 +157,93 @@ async_write_some(AsyncWriteStream& stream, serializer<
isRequest, Body, Fields, Decorator, Allocator>& sr, isRequest, Body, Fields, Decorator, Allocator>& sr,
WriteHandler&& handler); WriteHandler&& handler);
/** Write an HTTP/1 message to a stream. //------------------------------------------------------------------------------
This function is used to write a message to a stream. The call /** Write a header to a stream using a serializer.
will block until one of the following conditions is true:
@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. @li An error occurs.
This operation is implemented in terms of one or more calls This operation is implemented in terms of one or more calls
to the stream's `write_some` function. 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. @param stream The stream to which the data is to be written.
The type must support the @b SyncWriteStream concept. 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. @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, template<class SyncWriteStream,
bool isRequest, class Body, class Fields> bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
void void
write(SyncWriteStream& stream, write_header(SyncWriteStream& stream, serializer<
message<isRequest, Body, Fields> const& msg); 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 This function is used to write a header to a stream using a
will block until one of the following conditions is true: 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. @li An error occurs.
This operation is implemented in terms of one or more calls This operation is implemented in terms of one or more calls
to the stream's `write_some` function. 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. @param stream The stream to which the data is to be written.
The type must support the @b SyncWriteStream concept. 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, template<class SyncWriteStream,
bool isRequest, class Body, class Fields> bool isRequest, class Body, class Fields,
class Decorator, class Allocator>
void void
write(SyncWriteStream& stream, write_header(SyncWriteStream& stream, serializer<
message<isRequest, Body, Fields> const& msg, isRequest, Body, Fields, Decorator, Allocator>& sr,
error_code& ec); 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 This function is used to write a header to a stream asynchronously
a stream. The function call always returns immediately. The using a caller-provided HTTP/1 serializer. The function call always
asynchronous operation will continue until one of the following returns immediately. The asynchronous operation will continue until
conditions is true: 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. @li An error occurs.
This operation is implemented in terms of one or more calls to This operation is implemented in terms of zero or more calls to the stream's
the stream's `async_write_some` functions, and is known as a `async_write_some` function, and is known as a <em>composed operation</em>.
<em>composed operation</em>. The program must ensure that the The program must ensure that the stream performs no other write operations
stream performs no other write operations until this operation until this operation completes.
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`.
@param stream The stream to which the data is to be written. @param stream The stream to which the data is to be written.
The type must support the @b AsyncWriteStream concept. The type must support the @b AsyncWriteStream concept.
@param msg The message to write. The object must remain valid @param sr The serializer to use.
at least until the completion handler is called; ownership is
not transferred.
@param handler The handler to be called when the operation @param handler The handler to be called when the operation
completes. Copies will be made of the handler as required. 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 immediately or not, the handler will not be invoked from within
this function. Invocation of the handler will be performed in a this function. Invocation of the handler will be performed in a
manner equivalent to using `boost::asio::io_service::post`. 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, template<class AsyncWriteStream,
bool isRequest, class Body, class Fields, bool isRequest, class Body, class Fields,

View File

@ -20,7 +20,7 @@ namespace websocket {
namespace detail { namespace detail {
// A container that holds a suspended, asynchronous composed // 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. // resume the operation, or the container may be destroyed.
// //
class pausation class pausation

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,11 +10,14 @@
#include "test_parser.hpp" #include "test_parser.hpp"
#include <beast/core/ostream.hpp>
#include <beast/core/static_buffer.hpp>
#include <beast/http/fields.hpp> #include <beast/http/fields.hpp>
#include <beast/http/dynamic_body.hpp> #include <beast/http/dynamic_body.hpp>
#include <beast/http/header_parser.hpp> #include <beast/http/header_parser.hpp>
#include <beast/http/string_body.hpp> #include <beast/http/string_body.hpp>
#include <beast/test/fail_stream.hpp> #include <beast/test/fail_stream.hpp>
#include <beast/test/pipe_stream.hpp>
#include <beast/test/string_istream.hpp> #include <beast/test/string_istream.hpp>
#include <beast/test/yield_to.hpp> #include <beast/test/yield_to.hpp>
#include <beast/unit_test/suite.hpp> #include <beast/unit_test/suite.hpp>
@ -108,7 +111,7 @@ public:
{ {
multi_buffer b; multi_buffer b;
test::string_istream ss(ios_, "GET / X"); test::string_istream ss(ios_, "GET / X");
message_parser<true, dynamic_body, fields> p; request_parser<dynamic_body> p;
read(ss, b, p); read(ss, b, p);
fail(); 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) void testFailures(yield_context do_yield)
{ {
char const* req[] = { char const* req[] = {
@ -318,6 +367,7 @@ public:
run() override run() override
{ {
testThrow(); testThrow();
testBufferOverflow();
yield_to([&](yield_context yield){ yield_to([&](yield_context yield){
testFailures(yield); }); testFailures(yield); });

View File

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

View File

@ -9,6 +9,7 @@
#include <beast/http/type_traits.hpp> #include <beast/http/type_traits.hpp>
#include <beast/http/empty_body.hpp> #include <beast/http/empty_body.hpp>
#include <string>
namespace beast { namespace beast {
namespace http { 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_reader<empty_body>::value);
BOOST_STATIC_ASSERT(! is_body_writer<std::string>::value);
} // http } // http
} // beast } // beast

View File

@ -382,7 +382,7 @@ public:
m.body = "*****"; m.body = "*****";
error_code ec; error_code ec;
write(fs, m, ec); write(fs, m, ec);
if(ec == boost::asio::error::eof) if(ec == error::end_of_stream)
{ {
BEAST_EXPECT(fs.next_layer().str == BEAST_EXPECT(fs.next_layer().str ==
"GET / HTTP/1.0\r\n" "GET / HTTP/1.0\r\n"
@ -415,7 +415,7 @@ public:
m.body = "*****"; m.body = "*****";
error_code ec; error_code ec;
async_write(fs, m, do_yield[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 == BEAST_EXPECT(fs.next_layer().str ==
"GET / HTTP/1.0\r\n" "GET / HTTP/1.0\r\n"
@ -559,7 +559,7 @@ public:
test::string_ostream ss(ios_); test::string_ostream ss(ios_);
error_code ec; error_code ec;
write(ss, m, ec); write(ss, m, ec);
BEAST_EXPECT(ec == boost::asio::error::eof); BEAST_EXPECT(ec == error::end_of_stream);
BEAST_EXPECT(ss.str == BEAST_EXPECT(ss.str ==
"GET / HTTP/1.0\r\n" "GET / HTTP/1.0\r\n"
"User-Agent: test\r\n" "User-Agent: test\r\n"
@ -596,7 +596,7 @@ public:
test::string_ostream ss(ios_); test::string_ostream ss(ios_);
error_code ec; error_code ec;
write(ss, m, ec); write(ss, m, ec);
BEAST_EXPECT(ec == boost::asio::error::eof); BEAST_EXPECT(ec == error::end_of_stream);
BEAST_EXPECT(ss.str == BEAST_EXPECT(ss.str ==
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"User-Agent: test\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 void run() override
{ {
yield_to([&](yield_context yield){ yield_to([&](yield_context yield){
@ -1046,7 +959,6 @@ public:
test_std_ostream(); test_std_ostream();
testOstream(); testOstream();
testIoService(); testIoService();
testCgiRelay();
yield_to( yield_to(
[&](yield_context yield) [&](yield_context yield)
{ {