mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 21:07:26 +02:00
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:
@ -7,6 +7,7 @@ API Changes:
|
||||
|
||||
* Refactor treatment of request-method
|
||||
* Refactor treatment of status code and obsolete reason
|
||||
* Refactor HTTP serialization and parsing
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
13
README.md
13
README.md
@ -188,6 +188,7 @@ int main()
|
||||
|
||||
Example HTTP program:
|
||||
```C++
|
||||
#include <beast/core.hpp>
|
||||
#include <beast/http.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
@ -197,7 +198,7 @@ Example HTTP program:
|
||||
int main()
|
||||
{
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "boost.org";
|
||||
std::string const host = "www.example.com";
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r{ios};
|
||||
boost::asio::ip::tcp::socket sock{ios};
|
||||
@ -206,7 +207,7 @@ int main()
|
||||
|
||||
// Send HTTP request using beast
|
||||
beast::http::request<beast::http::string_body> req;
|
||||
req.method("GET");
|
||||
req.method(beast::http::verb::get);
|
||||
req.target("/");
|
||||
req.version = 11;
|
||||
req.fields.replace("Host", host + ":" +
|
||||
@ -216,10 +217,10 @@ int main()
|
||||
beast::http::write(sock, req);
|
||||
|
||||
// Receive and print HTTP response using beast
|
||||
beast::streambuf sb;
|
||||
beast::http::response<beast::http::dynamic_body> resp;
|
||||
beast::http::read(sock, sb, resp);
|
||||
std::cout << resp;
|
||||
beast::flat_buffer b;
|
||||
beast::http::response<beast::http::dynamic_body> res;
|
||||
beast::http::read(sock, b, res);
|
||||
std::cout << res << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -144,11 +144,21 @@ and which must remain valid until the operation is complete:
|
||||
][
|
||||
Send __serializer__ buffer data to a __SyncWriteStream__.
|
||||
]]
|
||||
[[
|
||||
[link beast.ref.http__write_header.overload1 [*write_header]]
|
||||
][
|
||||
Send an entire header from a __serializer__ to a __SyncWriteStream__.
|
||||
]]
|
||||
[[
|
||||
[link beast.ref.http__async_write_some [*async_write_some]]
|
||||
][
|
||||
Send some __serializer__ buffer data to an __AsyncWriteStream__.
|
||||
]]
|
||||
[[
|
||||
[link beast.ref.http__async_write_header [*async_write_header]]
|
||||
][
|
||||
Send an entire header from a __serializer__ to a __AsyncWriteStream__.
|
||||
]]
|
||||
]
|
||||
|
||||
Here is an example which synchronously sends a message on a stream using
|
||||
|
@ -27,7 +27,7 @@ Use HTTP to request the root page from a website and print the response:
|
||||
int main()
|
||||
{
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "boost.org";
|
||||
std::string const host = "www.example.com";
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r{ios};
|
||||
boost::asio::ip::tcp::socket sock{ios};
|
||||
@ -49,7 +49,7 @@ int main()
|
||||
beast::flat_buffer b;
|
||||
beast::http::response<beast::http::dynamic_body> res;
|
||||
beast::http::read(sock, b, res);
|
||||
std::cout << res;
|
||||
std::cout << res << std::endl;
|
||||
}
|
||||
```
|
||||
[heading WebSocket]
|
||||
|
@ -59,8 +59,10 @@
|
||||
<bridgehead renderas="sect3">Functions</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
<member><link linkend="beast.ref.http__async_read">async_read</link></member>
|
||||
<member><link linkend="beast.ref.http__async_read_header">async_read_header</link></member>
|
||||
<member><link linkend="beast.ref.http__async_read_some">async_read_some</link></member>
|
||||
<member><link linkend="beast.ref.http__async_write">async_write</link></member>
|
||||
<member><link linkend="beast.ref.http__async_write_header">async_write_header</link></member>
|
||||
<member><link linkend="beast.ref.http__async_write_some">async_write_some</link></member>
|
||||
<member><link linkend="beast.ref.http__is_keep_alive">is_keep_alive</link></member>
|
||||
<member><link linkend="beast.ref.http__is_upgrade">is_upgrade</link></member>
|
||||
@ -69,19 +71,15 @@
|
||||
<member><link linkend="beast.ref.http__operator_ls_">operator<<</link></member>
|
||||
<member><link linkend="beast.ref.http__prepare">prepare</link></member>
|
||||
<member><link linkend="beast.ref.http__read">read</link></member>
|
||||
<member><link linkend="beast.ref.http__read_header">read_header</link></member>
|
||||
<member><link linkend="beast.ref.http__read_some">read_some</link></member>
|
||||
<member><link linkend="beast.ref.http__string_to_verb">string_to_verb</link></member>
|
||||
<member><link linkend="beast.ref.http__swap">swap</link></member>
|
||||
<member><link linkend="beast.ref.http__to_string">to_string</link></member>
|
||||
<member><link linkend="beast.ref.http__write">write</link></member>
|
||||
<member><link linkend="beast.ref.http__write_header">write_header</link></member>
|
||||
<member><link linkend="beast.ref.http__write_some">write_some</link></member>
|
||||
</simplelist>
|
||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
<member><link linkend="beast.ref.http__is_body">is_body</link></member>
|
||||
<member><link linkend="beast.ref.http__is_body_writer">is_body_writer</link></member>
|
||||
<member><link linkend="beast.ref.http__is_body_reader">is_body_reader</link></member>
|
||||
</simplelist>
|
||||
</entry>
|
||||
<entry valign="top">
|
||||
<bridgehead renderas="sect3">Constants</bridgehead>
|
||||
@ -91,6 +89,12 @@
|
||||
<member><link linkend="beast.ref.http__status">status</link></member>
|
||||
<member><link linkend="beast.ref.http__verb">verb</link></member>
|
||||
</simplelist>
|
||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
<member><link linkend="beast.ref.http__is_body">is_body</link></member>
|
||||
<member><link linkend="beast.ref.http__is_body_writer">is_body_writer</link></member>
|
||||
<member><link linkend="beast.ref.http__is_body_reader">is_body_reader</link></member>
|
||||
</simplelist>
|
||||
<bridgehead renderas="sect3">Concepts</bridgehead>
|
||||
<simplelist type="vert" columns="1">
|
||||
<member><link linkend="beast.ref.Body">Body</link></member>
|
||||
|
@ -15,7 +15,7 @@
|
||||
int main()
|
||||
{
|
||||
// Normal boost::asio setup
|
||||
std::string const host = "boost.org";
|
||||
std::string const host = "www.example.com";
|
||||
boost::asio::io_service ios;
|
||||
boost::asio::ip::tcp::resolver r{ios};
|
||||
boost::asio::ip::tcp::socket sock{ios};
|
||||
@ -37,5 +37,5 @@ int main()
|
||||
beast::flat_buffer b;
|
||||
beast::http::response<beast::http::dynamic_body> res;
|
||||
beast::http::read(sock, b, res);
|
||||
std::cout << res;
|
||||
std::cout << res << std::endl;
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ private:
|
||||
buffer_type b;
|
||||
std::condition_variable cv;
|
||||
std::unique_ptr<read_op> op;
|
||||
bool eof = false;
|
||||
};
|
||||
|
||||
state s_[2];
|
||||
@ -151,6 +152,15 @@ public:
|
||||
std::size_t>::max)());
|
||||
}
|
||||
|
||||
/** Close the stream.
|
||||
|
||||
The other end of the pipe will see
|
||||
`boost::asio::error::eof` on read.
|
||||
*/
|
||||
template<class = void>
|
||||
void
|
||||
close();
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
read_some(MutableBufferSequence const& buffers);
|
||||
@ -275,22 +285,49 @@ read_op_impl<Handler, Buffers>::operator()()
|
||||
{
|
||||
BOOST_ASSERT(s_.in_.op);
|
||||
std::unique_lock<std::mutex> lock{s_.in_.m};
|
||||
BOOST_ASSERT(buffer_size(s_.in_.b.data()) > 0);
|
||||
auto const bytes_transferred = buffer_copy(
|
||||
b_, s_.in_.b.data(), s_.read_max_);
|
||||
s_.in_.b.consume(bytes_transferred);
|
||||
auto& s = s_;
|
||||
Handler h{std::move(h_)};
|
||||
lock.unlock();
|
||||
s.in_.op.reset(nullptr);
|
||||
++s.nread;
|
||||
s.ios_.post(bind_handler(std::move(h),
|
||||
error_code{}, bytes_transferred));
|
||||
if(s_.in_.b.size() > 0)
|
||||
{
|
||||
auto const bytes_transferred = buffer_copy(
|
||||
b_, s_.in_.b.data(), s_.read_max_);
|
||||
s_.in_.b.consume(bytes_transferred);
|
||||
auto& s = s_;
|
||||
Handler h{std::move(h_)};
|
||||
lock.unlock();
|
||||
s.in_.op.reset(nullptr);
|
||||
++s.nread;
|
||||
s.ios_.post(bind_handler(std::move(h),
|
||||
error_code{}, bytes_transferred));
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(s_.in_.eof);
|
||||
auto& s = s_;
|
||||
Handler h{std::move(h_)};
|
||||
lock.unlock();
|
||||
s.in_.op.reset(nullptr);
|
||||
++s.nread;
|
||||
s.ios_.post(bind_handler(std::move(h),
|
||||
boost::asio::error::eof, 0));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class>
|
||||
void
|
||||
pipe::stream::
|
||||
close()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{out_.m};
|
||||
out_.eof = true;
|
||||
if(out_.op)
|
||||
out_.op.get()->operator()();
|
||||
else
|
||||
out_.cv.notify_all();
|
||||
}
|
||||
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
pipe::stream::
|
||||
@ -318,19 +355,28 @@ read_some(MutableBufferSequence const& buffers,
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
BOOST_ASSERT(! in_.op);
|
||||
BOOST_ASSERT(buffer_size(buffers) > 0);
|
||||
if(fc_ && fc_->fail(ec))
|
||||
return 0;
|
||||
std::unique_lock<std::mutex> lock{in_.m};
|
||||
in_.cv.wait(lock,
|
||||
[&]()
|
||||
{
|
||||
return
|
||||
buffer_size(buffers) == 0 ||
|
||||
buffer_size(in_.b.data()) > 0;
|
||||
return in_.b.size() > 0 || in_.eof;
|
||||
});
|
||||
auto const bytes_transferred = buffer_copy(
|
||||
buffers, in_.b.data(), write_max_);
|
||||
in_.b.consume(bytes_transferred);
|
||||
std::size_t bytes_transferred;
|
||||
if(in_.b.size() > 0)
|
||||
{
|
||||
bytes_transferred = buffer_copy(
|
||||
buffers, in_.b.data(), write_max_);
|
||||
in_.b.consume(bytes_transferred);
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(in_.eof);
|
||||
bytes_transferred = 0;
|
||||
ec = boost::asio::error::eof;
|
||||
}
|
||||
++nread;
|
||||
return bytes_transferred;
|
||||
}
|
||||
@ -347,9 +393,10 @@ async_read_some(MutableBufferSequence const& buffers,
|
||||
"MutableBufferSequence requirements not met");
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
BOOST_ASSERT(! in_.op);
|
||||
BOOST_ASSERT(buffer_size(buffers) > 0);
|
||||
async_completion<ReadHandler,
|
||||
void(error_code, std::size_t)> init{handler};
|
||||
BOOST_ASSERT(! in_.op);
|
||||
if(fc_)
|
||||
{
|
||||
error_code ec;
|
||||
@ -359,7 +406,14 @@ async_read_some(MutableBufferSequence const& buffers,
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> lock{in_.m};
|
||||
if(buffer_size(buffers) == 0 ||
|
||||
if(in_.eof)
|
||||
{
|
||||
lock.unlock();
|
||||
++nread;
|
||||
ios_.post(bind_handler(init.completion_handler,
|
||||
boost::asio::error::eof, 0));
|
||||
}
|
||||
else if(buffer_size(buffers) == 0 ||
|
||||
buffer_size(in_.b.data()) > 0)
|
||||
{
|
||||
auto const bytes_transferred = buffer_copy(
|
||||
@ -389,6 +443,7 @@ write_some(ConstBufferSequence const& buffers)
|
||||
static_assert(is_const_buffer_sequence<
|
||||
ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
BOOST_ASSERT(! out_.eof);
|
||||
error_code ec;
|
||||
auto const bytes_transferred =
|
||||
write_some(buffers, ec);
|
||||
@ -408,6 +463,7 @@ write_some(
|
||||
"ConstBufferSequence requirements not met");
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
BOOST_ASSERT(! out_.eof);
|
||||
if(fc_ && fc_->fail(ec))
|
||||
return 0;
|
||||
auto const n = (std::min)(
|
||||
@ -437,6 +493,7 @@ async_write_some(ConstBufferSequence const& buffers,
|
||||
"ConstBufferSequence requirements not met");
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
BOOST_ASSERT(! out_.eof);
|
||||
async_completion<WriteHandler,
|
||||
void(error_code, std::size_t)> init{handler};
|
||||
if(fc_)
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace beast {
|
||||
namespace test {
|
||||
@ -32,7 +33,7 @@ protected:
|
||||
|
||||
private:
|
||||
boost::optional<boost::asio::io_service::work> work_;
|
||||
std::thread thread_;
|
||||
std::vector<std::thread> threads_;
|
||||
std::mutex m_;
|
||||
std::condition_variable cv_;
|
||||
std::size_t running_ = 0;
|
||||
@ -42,20 +43,21 @@ public:
|
||||
using yield_context =
|
||||
boost::asio::yield_context;
|
||||
|
||||
enable_yield_to()
|
||||
explicit
|
||||
enable_yield_to(std::size_t concurrency = 1)
|
||||
: work_(ios_)
|
||||
, thread_([&]
|
||||
{
|
||||
ios_.run();
|
||||
}
|
||||
)
|
||||
{
|
||||
threads_.reserve(concurrency);
|
||||
while(concurrency--)
|
||||
threads_.emplace_back(
|
||||
[&]{ ios_.run(); });
|
||||
}
|
||||
|
||||
~enable_yield_to()
|
||||
{
|
||||
work_ = boost::none;
|
||||
thread_.join();
|
||||
for(auto& t : threads_)
|
||||
t.join();
|
||||
}
|
||||
|
||||
/// Return the `io_service` associated with the object
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
|
||||
@ -24,9 +25,13 @@ read_size_helper(DynamicBuffer const& buffer, std::size_t max_size)
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(max_size >= 1);
|
||||
auto const size = buffer.size();
|
||||
return std::min<std::size_t>(
|
||||
std::max<std::size_t>(512, buffer.capacity() - size),
|
||||
std::min<std::size_t>(max_size, buffer.max_size() - size));
|
||||
auto const limit = buffer.max_size() - size;
|
||||
if(limit > 0)
|
||||
return std::min<std::size_t>(
|
||||
std::max<std::size_t>(512, buffer.capacity() - size),
|
||||
std::min<std::size_t>(max_size, limit));
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"dynamic buffer overflow"});
|
||||
}
|
||||
|
||||
} // detail
|
||||
|
@ -80,7 +80,7 @@ write_some(ConstBufferSequence const& buffers,
|
||||
buffer_size(buffer));
|
||||
if(os_.fail())
|
||||
{
|
||||
ec = errc::make_error_code(
|
||||
ec = make_error_code(
|
||||
errc::no_stream_resources);
|
||||
break;
|
||||
}
|
||||
|
@ -21,41 +21,6 @@
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Describes the parser's current state.
|
||||
|
||||
The state is expressed as the type of data that
|
||||
@ref basic_parser is expecting to see in subsequently
|
||||
provided octets.
|
||||
*/
|
||||
enum class parse_state
|
||||
{
|
||||
/// Expecting one or more header octets
|
||||
header = 0,
|
||||
|
||||
/// Expecting one or more body octets
|
||||
body = 1,
|
||||
|
||||
/// Expecting zero or more body octets followed by EOF
|
||||
body_to_eof = 2,
|
||||
|
||||
/// Expecting additional chunk header octets
|
||||
chunk_header = 3,
|
||||
|
||||
/// Expecting one or more chunk body octets
|
||||
chunk_body = 4,
|
||||
|
||||
/** The parsing is complete.
|
||||
|
||||
The parse is considered complete when the full header
|
||||
is received and either the full body is received, or
|
||||
the semantics of the message indicate that no body
|
||||
is expected. This includes the case where the caller
|
||||
has indicated to the parser that no body is expected,
|
||||
for example when receiving a response to a HEAD request.
|
||||
*/
|
||||
complete = 5
|
||||
};
|
||||
|
||||
/** A parser for decoding HTTP/1 wire format messages.
|
||||
|
||||
This parser is designed to efficiently parse messages in the
|
||||
@ -83,18 +48,13 @@ enum class parse_state
|
||||
struct derived
|
||||
: basic_parser<isRequest, derived<isRequest>>
|
||||
{
|
||||
// The type used when providing a mutable
|
||||
// buffer sequence in which to store body data.
|
||||
//
|
||||
using mutable_buffers_type = ...;
|
||||
|
||||
// When isRequest == true, called
|
||||
// after the Request Line is received.
|
||||
//
|
||||
void
|
||||
on_request(
|
||||
string_view const& method,
|
||||
string_view const& target,
|
||||
string_view method,
|
||||
string_view target,
|
||||
int version,
|
||||
error_code& ec);
|
||||
|
||||
@ -104,7 +64,7 @@ enum class parse_state
|
||||
void
|
||||
on_response(
|
||||
int status,
|
||||
string_view const& reason,
|
||||
string_view reason,
|
||||
int version,
|
||||
error_code& ec);
|
||||
|
||||
@ -112,8 +72,8 @@ enum class parse_state
|
||||
//
|
||||
void
|
||||
on_field(
|
||||
string_view const& name,
|
||||
string_view const& value,
|
||||
string_view name,
|
||||
string_view value,
|
||||
error_code& ec);
|
||||
|
||||
// Called after the header is complete.
|
||||
@ -123,40 +83,19 @@ enum class parse_state
|
||||
error_code& ec);
|
||||
|
||||
// Called once before the body, if any, is started.
|
||||
// This will only be called if the semantics of the
|
||||
// message indicate that a body exists, including
|
||||
// an indicated body of zero length.
|
||||
//
|
||||
void
|
||||
on_body();
|
||||
on_body(
|
||||
boost::optional<std::uint64_t> content_length,
|
||||
error_code& ec);
|
||||
|
||||
// Called zero or more times to provide body data.
|
||||
//
|
||||
// Only used if isDirect == false
|
||||
//
|
||||
void
|
||||
on_data(
|
||||
string_view const& s,
|
||||
string_view s,
|
||||
error_code& ec);
|
||||
|
||||
// Called zero or more times to retrieve a mutable
|
||||
// buffer sequence in which to store body data.
|
||||
//
|
||||
// Only used if isDirect == true
|
||||
//
|
||||
mutable_buffers_type
|
||||
on_prepare(
|
||||
std::size_t n);
|
||||
|
||||
// Called after body data has been stored in the
|
||||
// buffer returned by the previous call to on_prepare.
|
||||
//
|
||||
// Only used if isDirect == true
|
||||
//
|
||||
void
|
||||
on_commit(
|
||||
std::size_t n);
|
||||
|
||||
// If the Transfer-Encoding is specified, and the
|
||||
// last item in the list of encodings is "chunked",
|
||||
// called after receiving a chunk header or a final
|
||||
@ -164,8 +103,8 @@ enum class parse_state
|
||||
//
|
||||
void
|
||||
on_chunk(
|
||||
std::uint64_t length, // Length of this chunk
|
||||
string_view const& ext, // The chunk extensions, if any
|
||||
std::uint64_t length, // Length of this chunk
|
||||
string_view const& ext, // The chunk extensions, if any
|
||||
error_code& ec);
|
||||
|
||||
// Called once when the message is complete.
|
||||
@ -177,16 +116,7 @@ enum class parse_state
|
||||
@endcode
|
||||
|
||||
If a callback sets the error code, the error will be propagated
|
||||
to the caller of the parser. Behavior of parsing after an error
|
||||
is returned is undefined.
|
||||
|
||||
When the parser state is positioned to read bytes belonging to
|
||||
the body, calling @ref write or @ref write will implicitly
|
||||
cause a buffer copy (because bytes are first transferred to the
|
||||
dynamic buffer). To avoid this copy, the additional functions
|
||||
@ref copy_body, @ref prepare_body, and @ref commit_body are
|
||||
provided to allow the caller to read bytes directly into buffers
|
||||
supplied by the parser.
|
||||
to the caller of the parser.
|
||||
|
||||
The parser is optimized for the case where the input buffer
|
||||
sequence consists of a single contiguous buffer. The
|
||||
@ -203,26 +133,21 @@ enum class parse_state
|
||||
@tparam isRequest A `bool` indicating whether the parser will be
|
||||
presented with request or response message.
|
||||
|
||||
@tparam isDirect A `bool` indicating whether the parser interface
|
||||
supports reading body data directly into parser-provided buffers.
|
||||
|
||||
@tparam Derived The derived class type. This is part of the
|
||||
Curiously Recurring Template Pattern interface.
|
||||
*/
|
||||
template<bool isRequest, bool isDirect, class Derived>
|
||||
template<bool isRequest, class Derived>
|
||||
class basic_parser
|
||||
: private detail::basic_parser_base
|
||||
{
|
||||
template<bool OtherIsRequest,
|
||||
bool OtherIsDirect, class OtherDerived>
|
||||
template<bool OtherIsRequest, class OtherDerived>
|
||||
friend class basic_parser;
|
||||
|
||||
// Message will be complete after reading header
|
||||
static unsigned constexpr flagSkipBody = 1<< 0;
|
||||
|
||||
|
||||
|
||||
static unsigned constexpr flagOnBody = 1<< 1;
|
||||
// Consume input buffers across semantic boundaries
|
||||
static unsigned constexpr flagEager = 1<< 1;
|
||||
|
||||
// The parser has read at least one byte
|
||||
static unsigned constexpr flagGotSome = 1<< 2;
|
||||
@ -248,10 +173,8 @@ class basic_parser
|
||||
std::size_t buf_len_ = 0;
|
||||
std::size_t skip_ = 0; // search from here
|
||||
std::size_t x_; // scratch variable
|
||||
state state_ = state::nothing_yet;
|
||||
unsigned f_ = 0; // flags
|
||||
parse_state state_ = parse_state::header;
|
||||
string_view ext_;
|
||||
string_view body_;
|
||||
|
||||
public:
|
||||
/// Copy constructor (disallowed)
|
||||
@ -264,7 +187,8 @@ public:
|
||||
basic_parser() = default;
|
||||
|
||||
/// `true` if this parser parses requests, `false` for responses.
|
||||
static bool constexpr is_request = isRequest;
|
||||
using is_request =
|
||||
std::integral_constant<bool, isRequest>;
|
||||
|
||||
/// Destructor
|
||||
~basic_parser() = default;
|
||||
@ -274,83 +198,70 @@ public:
|
||||
After the move, the only valid operation on the
|
||||
moved-from object is destruction.
|
||||
*/
|
||||
template<bool OtherIsDirect, class OtherDerived>
|
||||
basic_parser(basic_parser<
|
||||
isRequest, OtherIsDirect, OtherDerived>&&);
|
||||
template<class OtherDerived>
|
||||
basic_parser(basic_parser<isRequest, OtherDerived>&&);
|
||||
|
||||
/** Set the skip body option.
|
||||
/** Returns a reference to this object as a @ref basic_parser.
|
||||
|
||||
The option controls whether or not the parser expects to
|
||||
see an HTTP body, regardless of the presence or absence of
|
||||
certain fields such as Content-Length.
|
||||
|
||||
Depending on the request, some responses do not carry a body.
|
||||
For example, a 200 response to a CONNECT request from a
|
||||
tunneling proxy. In these cases, callers may use this function
|
||||
inform the parser that no body is expected. The parser will
|
||||
consider the message complete after the header has been received.
|
||||
|
||||
@note This function must called before any bytes are processed.
|
||||
This is used to pass a derived class where a base class is
|
||||
expected, to choose a correct function overload when the
|
||||
resolution would be ambiguous.
|
||||
*/
|
||||
void
|
||||
skip_body();
|
||||
|
||||
/** Returns the current parser state.
|
||||
|
||||
The parser state indicates what octets the parser
|
||||
expects to see next in the input stream.
|
||||
*/
|
||||
parse_state
|
||||
state() const
|
||||
basic_parser&
|
||||
base()
|
||||
{
|
||||
return state_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns a constant reference to this object as a @ref basic_parser.
|
||||
|
||||
This is used to pass a derived class where a base class is
|
||||
expected, to choose a correct function overload when the
|
||||
resolution would be ambiguous.
|
||||
*/
|
||||
basic_parser const&
|
||||
base() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Returns `true` if the parser has received at least one byte of input.
|
||||
bool
|
||||
got_some() const
|
||||
{
|
||||
return (f_ & flagGotSome) != 0;
|
||||
}
|
||||
|
||||
/// Returns `true` if the complete header has been parsed.
|
||||
bool
|
||||
got_header() const
|
||||
{
|
||||
return state_ != parse_state::header;
|
||||
}
|
||||
|
||||
/** Returns `true` if a Content-Length is specified.
|
||||
|
||||
@note Only valid after parsing a complete header.
|
||||
*/
|
||||
bool
|
||||
got_content_length() const
|
||||
{
|
||||
return (f_ & flagContentLength) != 0;
|
||||
return state_ != state::nothing_yet;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is complete.
|
||||
|
||||
The message is complete after a full header is
|
||||
parsed and one of the following is true:
|
||||
The message is complete after the full header is prduced
|
||||
and one of the following is true:
|
||||
|
||||
@li @ref skip_body was called
|
||||
@li The skip body option was set.
|
||||
|
||||
@li The semantics of the message indicate there is no body.
|
||||
|
||||
@li The semantics of the message indicate a body is
|
||||
expected, and the entire body was received.
|
||||
@li The semantics of the message indicate a body is expected,
|
||||
and the entire body was parsed.
|
||||
*/
|
||||
bool
|
||||
is_complete() const
|
||||
is_done() const
|
||||
{
|
||||
return state_ == parse_state::complete;
|
||||
return state_ == state::complete;
|
||||
}
|
||||
|
||||
/** Returns `true` if a the parser has produced the full header.
|
||||
*/
|
||||
bool
|
||||
is_header_done() const
|
||||
{
|
||||
return state_ > state::header;
|
||||
}
|
||||
|
||||
/** Returns `true` if the message is an upgrade message.
|
||||
|
||||
@note Only valid after parsing a complete header.
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
is_upgrade() const
|
||||
@ -358,16 +269,10 @@ public:
|
||||
return (f_ & flagConnectionUpgrade) != 0;
|
||||
}
|
||||
|
||||
/** Returns `true` if keep-alive is specified
|
||||
/** Returns `true` if the last value for Transfer-Encoding is "chunked".
|
||||
|
||||
@note Only valid after parsing a complete header.
|
||||
*/
|
||||
bool
|
||||
is_keep_alive() const;
|
||||
|
||||
/** Returns `true` if the chunked Transfer-Encoding is specified.
|
||||
|
||||
@note Only valid after parsing a complete header.
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
is_chunked() const
|
||||
@ -375,34 +280,125 @@ public:
|
||||
return (f_ & flagChunked) != 0;
|
||||
}
|
||||
|
||||
/** Write part of a buffer sequence to the parser.
|
||||
/** Returns `true` if the message has keep-alive connection semantics.
|
||||
|
||||
This function attempts to parse the HTTP message
|
||||
stored in the caller provided buffers. Upon success,
|
||||
a positive return value indicates that the parser
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
bool
|
||||
is_keep_alive() const;
|
||||
|
||||
/** Returns the optional value of Content-Length if known.
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
boost::optional<std::uint64_t>
|
||||
content_length() const;
|
||||
|
||||
/** Returns `true` if the message semantics require an end of file.
|
||||
|
||||
Depending on the contents of the header, the parser may
|
||||
require and end of file notification to know where the end
|
||||
of the body lies. If this function returns `true` it will be
|
||||
necessary to call @ref put_eof when there will never be additional
|
||||
data from the input.
|
||||
*/
|
||||
bool
|
||||
need_eof() const
|
||||
{
|
||||
return (f_ & flagNeedEOF) != 0;
|
||||
}
|
||||
|
||||
/// Returns `true` if the eager parse option is set.
|
||||
bool
|
||||
eager() const
|
||||
{
|
||||
return (f_ & flagEager) != 0;
|
||||
}
|
||||
|
||||
/** Set the eager parse option.
|
||||
|
||||
This option controls whether or not the parser will attempt
|
||||
to consume all of the octets provided during parsing, or
|
||||
if it will stop when it reaches one of the following positions
|
||||
within the serialized message:
|
||||
|
||||
@li Immediately after the header
|
||||
|
||||
@li Immediately after any chunk header
|
||||
|
||||
The default is to stop after the header or any chunk header.
|
||||
*/
|
||||
void
|
||||
eager(bool v)
|
||||
{
|
||||
if(v)
|
||||
f_ |= flagEager;
|
||||
else
|
||||
f_ &= ~flagEager;
|
||||
}
|
||||
|
||||
/// Returns `true` if the parser will ignore the message body.
|
||||
bool
|
||||
skip()
|
||||
{
|
||||
return (f_ & flagSkipBody) != 0;
|
||||
}
|
||||
|
||||
/** Set the skip body option.
|
||||
|
||||
The option controls whether or not the parser expects to
|
||||
see an HTTP body, regardless of the presence or absence of
|
||||
certain fields such as Content-Length.
|
||||
|
||||
Depending on the request, some responses do not carry a
|
||||
body. For example, a 200 response to a CONNECT request
|
||||
from a tunneling proxy. In these cases, callers may use
|
||||
this function inform the parser that no body is expected.
|
||||
The parser will consider the message complete after the
|
||||
header has been received.
|
||||
|
||||
@note This function must called before any bytes are processed.
|
||||
*/
|
||||
void
|
||||
skip(bool v);
|
||||
|
||||
/** Write a buffer sequence to the parser.
|
||||
|
||||
This function attempts to incrementally parse the HTTP
|
||||
message data stored in the caller provided buffers. Upon
|
||||
success, a positive return value indicates that the parser
|
||||
made forward progress, consuming that number of
|
||||
bytes.
|
||||
|
||||
A return value of zero indicates that the parser
|
||||
requires additional input. In this case the caller
|
||||
should append additional bytes to the input buffer
|
||||
sequence and call @ref write again.
|
||||
In some cases there may be an insufficient number of octets
|
||||
in the input buffer in order to make forward progress. This
|
||||
is indicated by the the code @ref error::need_more. When
|
||||
this happens, the caller should place additional bytes into
|
||||
the buffer sequence and call @ref put again.
|
||||
|
||||
The error code @ref error::need_more is special. When this
|
||||
error is returned, a subsequent call to @ref put may succeed
|
||||
if the buffers have been updated. Otherwise, upon error
|
||||
the parser may not be restarted.
|
||||
|
||||
@param buffers An object meeting the requirements of
|
||||
@b ConstBufferSequence that represents the message.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@return The number of bytes consumed in the buffer
|
||||
sequence.
|
||||
@return The number of octets consumed in the buffer
|
||||
sequence. The caller should remove these octets even if the
|
||||
error is set.
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write(ConstBufferSequence const& buffers, error_code& ec);
|
||||
put(ConstBufferSequence const& buffers, error_code& ec);
|
||||
|
||||
#if ! BEAST_DOXYGEN
|
||||
std::size_t
|
||||
write(boost::asio::const_buffers_1 const& buffer,
|
||||
put(boost::asio::const_buffers_1 const& buffer,
|
||||
error_code& ec);
|
||||
#endif
|
||||
|
||||
@ -423,139 +419,7 @@ public:
|
||||
@param ec Set to the error, if any occurred.
|
||||
*/
|
||||
void
|
||||
write_eof(error_code& ec);
|
||||
|
||||
/** Returns the number of bytes remaining in the body or chunk.
|
||||
|
||||
If a Content-Length is specified and the parser state
|
||||
is equal to @ref beast::http::parse_state::body, this will return
|
||||
the number of bytes remaining in the body. If the
|
||||
chunked Transfer-Encoding is indicated and the parser
|
||||
state is equal to @ref beast::http::parse_state::chunk_body, this
|
||||
will return the number of bytes remaining in the chunk.
|
||||
Otherwise, the function behavior is undefined.
|
||||
*/
|
||||
std::uint64_t
|
||||
size() const
|
||||
{
|
||||
BOOST_ASSERT(
|
||||
state_ == parse_state::body ||
|
||||
state_ == parse_state::chunk_body);
|
||||
return len_;
|
||||
}
|
||||
|
||||
/** Returns the body data parsed in the last call to @ref write.
|
||||
|
||||
This buffer is invalidated after any call to @ref write
|
||||
or @ref write_eof.
|
||||
|
||||
@note If the last call to @ref write came from the input
|
||||
area of a @b DynamicBuffer object, a call to the dynamic
|
||||
buffer's `consume` function may invalidate this return
|
||||
value.
|
||||
*/
|
||||
string_view const&
|
||||
body() const
|
||||
{
|
||||
// This function not available when isDirect==true
|
||||
BOOST_STATIC_ASSERT(! isDirect);
|
||||
return body_;
|
||||
}
|
||||
|
||||
/** Returns the chunk extension parsed in the last call to @ref write.
|
||||
|
||||
This buffer is invalidated after any call to @ref write
|
||||
or @ref write_eof.
|
||||
|
||||
@note If the last call to @ref write came from the input
|
||||
area of a @b DynamicBuffer object, a call to the dynamic
|
||||
buffer's `consume` function may invalidate this return
|
||||
value.
|
||||
*/
|
||||
string_view const&
|
||||
chunk_extension() const
|
||||
{
|
||||
// This function not available when isDirect==true
|
||||
BOOST_STATIC_ASSERT(! isDirect);
|
||||
return ext_;
|
||||
}
|
||||
|
||||
/** Returns the optional value of Content-Length if known.
|
||||
|
||||
@note The return value is undefined unless a complete
|
||||
header has been parsed.
|
||||
*/
|
||||
boost::optional<std::uint64_t>
|
||||
content_length() const
|
||||
{
|
||||
BOOST_ASSERT(got_header());
|
||||
if(! (f_ & flagContentLength))
|
||||
return boost::none;
|
||||
return len_;
|
||||
}
|
||||
|
||||
/** Copy leftover body data from the dynamic buffer.
|
||||
|
||||
@note This member function is only available when
|
||||
`isDirect==true`.
|
||||
|
||||
@return The number of bytes processed from the dynamic
|
||||
buffer. The caller should remove these bytes by calling
|
||||
`consume` on the buffer.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
std::size_t
|
||||
copy_body(DynamicBuffer& buffer);
|
||||
|
||||
/** Returns a set of buffers for storing body data.
|
||||
|
||||
@param buffers A writable output parameter into which
|
||||
the function will place the buffers upon success.
|
||||
|
||||
@param limit The maximum number of bytes in the
|
||||
size of the returned buffer sequence. The actual size
|
||||
of the buffer sequence may be lower than this number.
|
||||
|
||||
@note This member function is only available when
|
||||
`isDirect==true`.
|
||||
*/
|
||||
template<class MutableBufferSequence>
|
||||
void
|
||||
prepare_body(boost::optional<
|
||||
MutableBufferSequence>& buffers, std::size_t limit);
|
||||
|
||||
/** Commit body data.
|
||||
|
||||
@param n The number of bytes to commit. This must
|
||||
be less than or equal to the size of the buffer
|
||||
sequence returned by @ref prepare_body.
|
||||
|
||||
@note This member function is only available when
|
||||
`isDirect==true`.
|
||||
*/
|
||||
void
|
||||
commit_body(std::size_t n);
|
||||
|
||||
/** Indicate that body octets have been consumed.
|
||||
|
||||
@param n The number of bytes to consume.
|
||||
*/
|
||||
void
|
||||
consume(std::size_t n)
|
||||
{
|
||||
BOOST_ASSERT(n <= len_);
|
||||
BOOST_ASSERT(
|
||||
state_ == parse_state::body ||
|
||||
state_ == parse_state::chunk_body);
|
||||
len_ -= n;
|
||||
if(len_ == 0)
|
||||
{
|
||||
if(state_ == parse_state::body)
|
||||
state_ = parse_state::complete;
|
||||
else
|
||||
state_ = parse_state::chunk_header;
|
||||
}
|
||||
}
|
||||
put_eof(error_code& ec);
|
||||
|
||||
private:
|
||||
inline
|
||||
@ -570,68 +434,42 @@ private:
|
||||
maybe_flatten(
|
||||
ConstBufferSequence const& buffers);
|
||||
|
||||
std::size_t
|
||||
do_write(boost::asio::const_buffers_1 const& buffer,
|
||||
void
|
||||
parse_header(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_header(char const*& p, char const* term,
|
||||
error_code& ec, std::true_type);
|
||||
|
||||
std::size_t
|
||||
do_write(boost::asio::const_buffers_1 const& buffer,
|
||||
void
|
||||
parse_header(char const*& p, char const* term,
|
||||
error_code& ec, std::false_type);
|
||||
|
||||
void
|
||||
parse_startline(char const*& it,
|
||||
int& version, int& status,
|
||||
error_code& ec, std::true_type);
|
||||
parse_body(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_startline(char const*& it,
|
||||
int& version, int& status,
|
||||
error_code& ec, std::false_type);
|
||||
parse_body_to_eof(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_fields(char const*& it,
|
||||
parse_chunk_header(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_chunk_body(char const*& p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
parse_fields(char const*& p,
|
||||
char const* last, error_code& ec);
|
||||
|
||||
void
|
||||
do_field(
|
||||
string_view const& name,
|
||||
string_view const& value,
|
||||
error_code& ec);
|
||||
|
||||
std::size_t
|
||||
parse_header(char const* p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
do_header(int, std::true_type);
|
||||
|
||||
void
|
||||
do_header(int status, std::false_type);
|
||||
|
||||
void
|
||||
maybe_do_body_direct();
|
||||
|
||||
void
|
||||
maybe_do_body_indirect(error_code& ec);
|
||||
|
||||
std::size_t
|
||||
parse_chunk_header(char const* p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
std::size_t
|
||||
parse_body(char const* p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
std::size_t
|
||||
parse_body_to_eof(char const* p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
std::size_t
|
||||
parse_chunk_body(char const* p,
|
||||
std::size_t n, error_code& ec);
|
||||
|
||||
void
|
||||
do_complete(error_code& ec);
|
||||
do_field(string_view const& name,
|
||||
string_view const& value,
|
||||
error_code& ec);
|
||||
};
|
||||
|
||||
} // http
|
||||
|
@ -8,9 +8,9 @@
|
||||
#ifndef BEAST_HTTP_BUFFER_BODY_HPP
|
||||
#define BEAST_HTTP_BUFFER_BODY_HPP
|
||||
|
||||
#include <beast/http/type_traits.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/type_traits.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@ -31,41 +31,30 @@ namespace http {
|
||||
...
|
||||
}
|
||||
@endcode
|
||||
|
||||
@tparam isDeferred A `bool` which, when set to `true`,
|
||||
indicates to the serialization implementation that it should
|
||||
send the entire HTTP Header before attempting to acquire
|
||||
buffers representing the body.
|
||||
|
||||
@tparam ConstBufferSequence The type of buffer sequence
|
||||
stored in the body by the caller.
|
||||
*/
|
||||
template<
|
||||
bool isDeferred,
|
||||
class ConstBufferSequence>
|
||||
struct buffer_body
|
||||
{
|
||||
static_assert(is_const_buffer_sequence<ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
/// The type of the body member when used in a message.
|
||||
struct value_type
|
||||
{
|
||||
/** A pointer to a contiguous area of memory of @ref size octets, else `nullptr`.
|
||||
|
||||
/** The type of the body member when used in a message.
|
||||
If this is `nullptr` and @ref more is `true`, the error
|
||||
@ref error::need_buffer will be returned by a serializer
|
||||
when attempting to retrieve the next buffer.
|
||||
*/
|
||||
void* data;
|
||||
|
||||
When engaged, the first element of the pair represents
|
||||
the current buffer sequence to be written.
|
||||
/** The number of octets in the buffer pointed to by @ref data
|
||||
|
||||
The second element of the pair indicates whether or not
|
||||
additional buffers will be available. A value of `false`
|
||||
indicates the end of the message body.
|
||||
If @ref data is `nullptr` during serialization, this value
|
||||
is not inspected.
|
||||
*/
|
||||
std::size_t size;
|
||||
|
||||
If the buffer in the value is disengaged, and the second
|
||||
element of the pair is `true`, @ref serializer operations
|
||||
will return @ref http::error::need_more. This signals the
|
||||
calling code that a new buffer should be placed into the
|
||||
body, or that the caller should indicate that no more
|
||||
buffers will be available.
|
||||
*/
|
||||
using value_type =
|
||||
std::pair<boost::optional<ConstBufferSequence>, bool>;
|
||||
/// `true` if this is not the last buffer.
|
||||
bool more;
|
||||
};
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm to obtain buffers representing the body
|
||||
@ -77,10 +66,10 @@ struct buffer_body
|
||||
value_type const& body_;
|
||||
|
||||
public:
|
||||
using is_deferred =
|
||||
std::integral_constant<bool, isDeferred>;
|
||||
using is_deferred = std::false_type;
|
||||
|
||||
using const_buffers_type = ConstBufferSequence;
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffers_1;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
@ -95,44 +84,89 @@ struct buffer_body
|
||||
{
|
||||
}
|
||||
|
||||
boost::optional<std::pair<ConstBufferSequence, bool>>
|
||||
boost::optional<
|
||||
std::pair<const_buffers_type, bool>>
|
||||
get(error_code& ec)
|
||||
{
|
||||
if(toggle_)
|
||||
{
|
||||
if(body_.second)
|
||||
if(body_.more)
|
||||
{
|
||||
toggle_ = false;
|
||||
ec = error::need_more;
|
||||
ec = error::need_buffer;
|
||||
}
|
||||
return boost::none;
|
||||
}
|
||||
if(body_.first)
|
||||
if(body_.data)
|
||||
{
|
||||
toggle_ = true;
|
||||
return {{*body_.first, body_.second}};
|
||||
return {{const_buffers_type{
|
||||
body_.data, body_.size}, body_.more}};
|
||||
}
|
||||
if(body_.second)
|
||||
ec = error::need_more;
|
||||
if(body_.more)
|
||||
ec = error::need_buffer;
|
||||
return boost::none;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm used store buffers in this body
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, buffer_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<std::uint64_t>, error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::buffer_copy;
|
||||
auto const n = buffer_size(buffers);
|
||||
if(! body_.data || n > body_.size)
|
||||
{
|
||||
ec = error::need_buffer;
|
||||
return;
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
buffer_copy(boost::asio::buffer(
|
||||
body_.data, body_.size), buffers);
|
||||
body_.data = reinterpret_cast<char*>(
|
||||
body_.data) + bytes_transferred;
|
||||
body_.size -= bytes_transferred;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
#if ! BEAST_DOXYGEN
|
||||
|
||||
// operator<< is not supported for buffer_body"
|
||||
template<bool isRequest, class Fields,
|
||||
bool isDeferred, class ConstBufferSequence>
|
||||
std::ostream&
|
||||
operator<<(std::ostream& os, message<
|
||||
isRequest, buffer_body<isDeferred,
|
||||
ConstBufferSequence>, Fields> const& msg)
|
||||
{
|
||||
static_assert(sizeof(ConstBufferSequence) == -1,
|
||||
"operator<< is not supported for buffer_body");
|
||||
}
|
||||
|
||||
operator<<(std::ostream& os, message<isRequest,
|
||||
buffer_body, Fields> const& msg) = delete;
|
||||
#endif
|
||||
|
||||
} // http
|
||||
|
@ -62,6 +62,20 @@ namespace detail {
|
||||
class basic_parser_base
|
||||
{
|
||||
protected:
|
||||
enum class state
|
||||
{
|
||||
nothing_yet = 0,
|
||||
header,
|
||||
body0,
|
||||
body,
|
||||
body_to_eof0,
|
||||
body_to_eof,
|
||||
chunk_header0,
|
||||
chunk_header,
|
||||
chunk_body,
|
||||
complete
|
||||
};
|
||||
|
||||
static
|
||||
bool
|
||||
is_pathchar(char c)
|
||||
@ -263,11 +277,9 @@ protected:
|
||||
bool
|
||||
parse_crlf(char const*& it)
|
||||
{
|
||||
if(*it != '\r')
|
||||
if( it[0] != '\r' && it[1] != '\n')
|
||||
return false;
|
||||
if(*++it != '\n')
|
||||
return false;
|
||||
++it;
|
||||
it += 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,8 @@
|
||||
#define BEAST_HTTP_DYNAMIC_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/multi_buffer.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <utility>
|
||||
@ -28,57 +28,6 @@ struct basic_dynamic_body
|
||||
/// The type of the body member when used in a message.
|
||||
using value_type = DynamicBuffer;
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm used store buffers in this body
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
static bool constexpr is_direct = true;
|
||||
|
||||
using mutable_buffers_type =
|
||||
typename DynamicBuffer::mutable_buffers_type;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest,
|
||||
basic_dynamic_body, Fields>& msg)
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(std::uint64_t content_length)
|
||||
{
|
||||
}
|
||||
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n)
|
||||
{
|
||||
return body_.prepare(n);
|
||||
}
|
||||
|
||||
void
|
||||
commit(std::size_t n)
|
||||
{
|
||||
body_.commit(n);
|
||||
}
|
||||
|
||||
void
|
||||
finish()
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm to obtain buffers representing the body
|
||||
using reader = implementation_defined;
|
||||
@ -119,6 +68,58 @@ struct basic_dynamic_body
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm used store buffers in this body
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest,
|
||||
basic_dynamic_body, Fields>& msg)
|
||||
: body_(msg.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> const&, error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> b;
|
||||
try
|
||||
{
|
||||
b.emplace(body_.prepare(
|
||||
buffer_size(buffers)));
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return;
|
||||
}
|
||||
body_.commit(buffer_copy(*b, buffers));
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
/** A dynamic message body represented by a @ref multi_buffer
|
||||
|
@ -9,6 +9,7 @@
|
||||
#define BEAST_HTTP_EMPTY_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
@ -26,6 +27,8 @@ struct empty_body
|
||||
/// The type of the body member when used in a message.
|
||||
struct value_type
|
||||
{
|
||||
// VFALCO We could stash boost::optional<std::uint64_t>
|
||||
// for the content length here, set on init()
|
||||
};
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
@ -64,6 +67,39 @@ struct empty_body
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm used store buffers in this body
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
struct writer
|
||||
{
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, empty_body, Fields>& msg)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<std::uint64_t> const&,
|
||||
error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
put(ConstBufferSequence const&,
|
||||
error_code& ec)
|
||||
{
|
||||
ec = error::missing_body;
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // http
|
||||
|
@ -14,36 +14,64 @@
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/// Error codes returned from HTTP parsing
|
||||
/// Error codes returned from HTTP algorithms and operations.
|
||||
enum class error
|
||||
{
|
||||
/** The end of the stream was reached.
|
||||
|
||||
This error is returned by @ref basic_parser::write_eof
|
||||
when the end of stream is reached and there are no
|
||||
unparsed bytes in the stream buffer.
|
||||
This error is returned under the following conditions:
|
||||
|
||||
@li When attempting to read HTTP data from a stream and the stream
|
||||
read returns the error `boost::asio::error::eof` before any new octets
|
||||
have been received.
|
||||
|
||||
@li When sending a complete HTTP message at once and the semantics of
|
||||
the message are that the connection should be closed to indicate the
|
||||
end of the message.
|
||||
*/
|
||||
end_of_stream = 1,
|
||||
|
||||
/** The incoming message is incomplete.
|
||||
|
||||
This happens when the end of stream is reached
|
||||
and some bytes have been received, but not the
|
||||
This happens when the end of stream is reached during
|
||||
parsing and some octets have been received, but not the
|
||||
entire message.
|
||||
*/
|
||||
partial_message,
|
||||
|
||||
/** Additional buffers are required.
|
||||
|
||||
This error is generated during serialization of HTTP
|
||||
messages using the @ref buffer_body representation.
|
||||
It indicates to the caller that an additional buffer
|
||||
sequence should be placed into the body, or that the
|
||||
caller should indicate that there are no more bytes
|
||||
remaining in the body to serialize.
|
||||
This error is returned during parsing when additional
|
||||
octets are needed. The caller should append more data
|
||||
to the existing buffer and retry the parse operaetion.
|
||||
*/
|
||||
need_more,
|
||||
|
||||
/** The message container has no body.
|
||||
|
||||
This error is returned when attempting to parse body
|
||||
octets into a message container which has the
|
||||
@ref empty_body body type.
|
||||
|
||||
@see @ref empty_body
|
||||
*/
|
||||
missing_body,
|
||||
|
||||
/** Additional buffers are required.
|
||||
|
||||
This error is returned under the following conditions:
|
||||
|
||||
@li During serialization when using @ref buffer_body.
|
||||
The caller should update the body to point to a new
|
||||
buffer or indicate that there are no more octets in
|
||||
the body.
|
||||
|
||||
@li During parsing when using @ref buffer_body.
|
||||
The caller should update the body to point to a new
|
||||
storage area to receive additional body octets.
|
||||
*/
|
||||
need_buffer,
|
||||
|
||||
/** Buffer maximum exceeded.
|
||||
|
||||
This error is returned when reading HTTP content
|
||||
@ -52,6 +80,10 @@ enum class error
|
||||
*/
|
||||
buffer_overflow,
|
||||
|
||||
//
|
||||
// (parser errors)
|
||||
//
|
||||
|
||||
/// The line ending was malformed
|
||||
bad_line_ending,
|
||||
|
||||
|
@ -33,18 +33,19 @@ namespace http {
|
||||
*/
|
||||
template<bool isRequest, class Fields>
|
||||
class header_parser
|
||||
: public basic_parser<isRequest, false,
|
||||
: public basic_parser<isRequest,
|
||||
header_parser<isRequest, Fields>>
|
||||
{
|
||||
header<isRequest, Fields> h_;
|
||||
string_view body_;
|
||||
|
||||
public:
|
||||
using mutable_buffers_type =
|
||||
boost::asio::null_buffers;
|
||||
|
||||
/// The type of @ref header this object produces.
|
||||
using value_type = header<isRequest, Fields>;
|
||||
|
||||
/// Default constructor.
|
||||
header_parser() = default;
|
||||
|
||||
/// Copy constructor.
|
||||
header_parser(header_parser const&) = default;
|
||||
|
||||
@ -58,18 +59,49 @@ public:
|
||||
*/
|
||||
header_parser(header_parser&&) = default;
|
||||
|
||||
/** Move assignment
|
||||
|
||||
After the move, the only valid operation
|
||||
on the moved-from object is destruction.
|
||||
*/
|
||||
header_parser& operator=(header_parser&&) = default;
|
||||
|
||||
/** Constructor
|
||||
|
||||
@param args If present, additional arguments to be
|
||||
forwarded to the @ref beast::http::header constructor.
|
||||
@param args Optional arguments forwarded
|
||||
forwarded to the @ref http::header constructor.
|
||||
*/
|
||||
#if BEAST_DOXYGEN
|
||||
template<class... Args>
|
||||
explicit
|
||||
header_parser(Args&&... args);
|
||||
#else
|
||||
template<class Arg0, class... ArgN,
|
||||
class = typename std::enable_if<
|
||||
! std::is_convertible<typename
|
||||
std::decay<Arg0>::type,
|
||||
header_parser>::value>>
|
||||
explicit
|
||||
header_parser(Arg0&& arg0, ArgN&&... argn);
|
||||
#endif
|
||||
|
||||
/** Returns parsed body octets.
|
||||
|
||||
This function will return the most recent buffer
|
||||
of octets corresponding to the parsed body. This
|
||||
buffer will become invalidated on any subsequent
|
||||
call to @ref put or @ref put_eof
|
||||
*/
|
||||
string_view
|
||||
body() const
|
||||
{
|
||||
return body_;
|
||||
}
|
||||
|
||||
/** Returns the parsed header
|
||||
|
||||
Only valid if @ref got_header would return `true`.
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
value_type const&
|
||||
get() const
|
||||
@ -79,7 +111,8 @@ public:
|
||||
|
||||
/** Returns the parsed header.
|
||||
|
||||
Only valid if @ref got_header would return `true`.
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
*/
|
||||
value_type&
|
||||
get()
|
||||
@ -89,8 +122,10 @@ public:
|
||||
|
||||
/** Returns ownership of the parsed header.
|
||||
|
||||
Ownership is transferred to the caller. Only
|
||||
valid if @ref got_header would return `true`.
|
||||
Ownership is transferred to the caller.
|
||||
|
||||
@note The return value is undefined unless
|
||||
@ref is_header_done would return `true`.
|
||||
|
||||
Requires:
|
||||
@ref value_type is @b MoveConstructible
|
||||
@ -98,20 +133,18 @@ public:
|
||||
value_type
|
||||
release()
|
||||
{
|
||||
static_assert(std::is_move_constructible<decltype(h_)>::value,
|
||||
static_assert(
|
||||
std::is_move_constructible<decltype(h_)>::value,
|
||||
"MoveConstructible requirements not met");
|
||||
return std::move(h_);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class basic_parser<
|
||||
isRequest, false, header_parser>;
|
||||
friend class basic_parser<isRequest, header_parser>;
|
||||
|
||||
void
|
||||
on_request(
|
||||
string_view const& method,
|
||||
string_view const& path,
|
||||
int version, error_code&)
|
||||
on_request(string_view method,
|
||||
string_view path, int version, error_code&)
|
||||
{
|
||||
h_.target(path);
|
||||
h_.method(method);
|
||||
@ -119,9 +152,8 @@ private:
|
||||
}
|
||||
|
||||
void
|
||||
on_response(int status,
|
||||
string_view const& reason,
|
||||
int version, error_code&)
|
||||
on_response(int status, string_view reason,
|
||||
int version, error_code&)
|
||||
{
|
||||
h_.status = status;
|
||||
h_.version = version;
|
||||
@ -129,9 +161,8 @@ private:
|
||||
}
|
||||
|
||||
void
|
||||
on_field(string_view const& name,
|
||||
string_view const& value,
|
||||
error_code&)
|
||||
on_field(string_view name,
|
||||
string_view value, error_code&)
|
||||
{
|
||||
h_.fields.insert(name, value);
|
||||
}
|
||||
@ -142,41 +173,28 @@ private:
|
||||
}
|
||||
|
||||
void
|
||||
on_body(error_code& ec)
|
||||
on_body(boost::optional<std::
|
||||
uint64_t> const&, error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_body(std::uint64_t content_length,
|
||||
error_code& ec)
|
||||
on_data(string_view s, error_code&)
|
||||
{
|
||||
body_ = s;
|
||||
}
|
||||
|
||||
void
|
||||
on_data(string_view const& s,
|
||||
error_code& ec)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_commit(std::size_t n)
|
||||
{
|
||||
// Can't write body data with header-only parser!
|
||||
BOOST_ASSERT(false);
|
||||
BOOST_THROW_EXCEPTION(std::logic_error{
|
||||
"invalid member function call"});
|
||||
}
|
||||
|
||||
void
|
||||
on_chunk(std::uint64_t n,
|
||||
string_view const& ext,
|
||||
error_code& ec)
|
||||
on_chunk(std::uint64_t,
|
||||
string_view const&, error_code&)
|
||||
{
|
||||
body_ = {};
|
||||
}
|
||||
|
||||
void
|
||||
on_complete(error_code&)
|
||||
{
|
||||
body_ = {};
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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
@ -12,10 +12,11 @@ namespace beast {
|
||||
namespace http {
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
template<class... Args>
|
||||
template<class Arg0, class... ArgN, class>
|
||||
header_parser<isRequest, Fields>::
|
||||
header_parser(Args&&... args)
|
||||
: h_(std::forward<Args>(args)...)
|
||||
header_parser(Arg0&& arg0, ArgN&&... argn)
|
||||
: h_(std::forward<Arg0>(arg0),
|
||||
std::forward<ArgN>(argn)...)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,7 @@ message_parser<isRequest, Body, Fields>::
|
||||
message_parser(header_parser<
|
||||
isRequest, Fields>&& parser, Args&&... args)
|
||||
: base_type(std::move(static_cast<basic_parser<
|
||||
isRequest, false, header_parser<
|
||||
isRequest, Fields>>&>(parser)))
|
||||
isRequest, header_parser<isRequest, Fields>>&>(parser)))
|
||||
, m_(parser.release(), std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
@ -28,159 +28,396 @@ namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
inline
|
||||
std::size_t
|
||||
read_some_buffer(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
error_code& ec)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Handler>
|
||||
class read_some_op
|
||||
{
|
||||
std::size_t bytes_used;
|
||||
if(buffer.size() == 0)
|
||||
goto do_read;
|
||||
for(;;)
|
||||
int state_ = 0;
|
||||
std::size_t used_ = 0;
|
||||
Stream& s_;
|
||||
DynamicBuffer& b_;
|
||||
basic_parser<isRequest, Derived>& p_;
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> mb_;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
read_some_op(read_some_op&&) = default;
|
||||
read_some_op(read_some_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
read_some_op(DeducedHandler&& h, Stream& s,
|
||||
DynamicBuffer& b, basic_parser<isRequest, Derived>& p)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, p_(p)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
bytes_used = parser.write(
|
||||
buffer.data(), ec);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, std::size_t bytes_transferred);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->state_ >= 2 ? true :
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_some_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Handler>
|
||||
void
|
||||
read_some_op<Stream, DynamicBuffer,
|
||||
isRequest, Derived, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
switch(state_)
|
||||
{
|
||||
case 0:
|
||||
state_ = 1;
|
||||
if(b_.size() == 0)
|
||||
goto do_read;
|
||||
goto do_parse;
|
||||
|
||||
case 1:
|
||||
state_ = 2;
|
||||
case 2:
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
if(p_.got_some())
|
||||
{
|
||||
// caller sees EOF on next read
|
||||
ec = {};
|
||||
p_.put_eof(ec);
|
||||
if(ec)
|
||||
goto upcall;
|
||||
BOOST_ASSERT(p_.is_done());
|
||||
goto upcall;
|
||||
}
|
||||
ec = error::end_of_stream;
|
||||
goto upcall;
|
||||
}
|
||||
if(ec)
|
||||
return 0;
|
||||
if(bytes_used > 0)
|
||||
goto do_finish;
|
||||
goto upcall;
|
||||
b_.commit(bytes_transferred);
|
||||
|
||||
do_parse:
|
||||
used_ += p_.put(b_.data(), ec);
|
||||
if(! ec || ec != http::error::need_more)
|
||||
goto do_upcall;
|
||||
ec = {};
|
||||
// [[fallthrough]]
|
||||
|
||||
do_read:
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> mb;
|
||||
using beast::detail::read_size_helper;
|
||||
auto const size =
|
||||
read_size_helper(buffer, 65536);
|
||||
BOOST_ASSERT(size > 0);
|
||||
try
|
||||
{
|
||||
mb.emplace(buffer.prepare(size));
|
||||
using beast::detail::read_size_helper;
|
||||
mb_.emplace(b_.prepare(
|
||||
read_size_helper(b_, 65536)));
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return 0;
|
||||
goto do_upcall;
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
stream.read_some(*mb, ec);
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
bytes_used = 0;
|
||||
if(parser.got_some())
|
||||
{
|
||||
// caller sees EOF on next read
|
||||
ec = {};
|
||||
parser.write_eof(ec);
|
||||
if(ec)
|
||||
return 0;
|
||||
BOOST_ASSERT(parser.is_complete());
|
||||
break;
|
||||
}
|
||||
ec = error::end_of_stream;
|
||||
break;
|
||||
}
|
||||
else if(ec)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
BOOST_ASSERT(bytes_transferred > 0);
|
||||
buffer.commit(bytes_transferred);
|
||||
return s_.async_read_some(*mb_, std::move(*this));
|
||||
|
||||
do_upcall:
|
||||
if(state_ >= 2)
|
||||
goto upcall;
|
||||
state_ = 3;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
|
||||
case 3:
|
||||
break;
|
||||
}
|
||||
do_finish:
|
||||
return bytes_used;
|
||||
upcall:
|
||||
h_(ec, used_);
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
inline
|
||||
std::size_t
|
||||
read_some_body(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, true, Derived>& parser,
|
||||
error_code& ec)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct parser_is_done
|
||||
{
|
||||
if(buffer.size() > 0)
|
||||
return parser.copy_body(buffer);
|
||||
boost::optional<typename
|
||||
Derived::mutable_buffers_type> mb;
|
||||
try
|
||||
template<bool isRequest, class Derived>
|
||||
bool
|
||||
operator()(basic_parser<
|
||||
isRequest, Derived> const& p) const
|
||||
{
|
||||
parser.prepare_body(mb, 65536);
|
||||
return p.is_done();
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
};
|
||||
|
||||
struct parser_is_header_done
|
||||
{
|
||||
template<bool isRequest, class Derived>
|
||||
bool
|
||||
operator()(basic_parser<
|
||||
isRequest, Derived> const& p) const
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return 0;
|
||||
return p.is_header_done();
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
stream.read_some(*mb, ec);
|
||||
if(ec == boost::asio::error::eof)
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Condition,
|
||||
class Handler>
|
||||
class read_op
|
||||
{
|
||||
int state_ = 0;
|
||||
Stream& s_;
|
||||
DynamicBuffer& b_;
|
||||
basic_parser<isRequest, Derived>& p_;
|
||||
Handler h_;
|
||||
|
||||
public:
|
||||
read_op(read_op&&) = default;
|
||||
read_op(read_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
read_op(DeducedHandler&& h, Stream& s,
|
||||
DynamicBuffer& b, basic_parser<isRequest,
|
||||
Derived>& p)
|
||||
: s_(s)
|
||||
, b_(b)
|
||||
, p_(p)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
// caller sees EOF on next read
|
||||
ec = {};
|
||||
parser.write_eof(ec);
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec, std::size_t bytes_used);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->state_ >= 3 ? true :
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Derived, class Condition,
|
||||
class Handler>
|
||||
void
|
||||
read_op<Stream, DynamicBuffer,
|
||||
isRequest, Derived, Condition, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_used)
|
||||
{
|
||||
switch(state_)
|
||||
{
|
||||
case 0:
|
||||
if(Condition{}(p_))
|
||||
{
|
||||
state_ = 1;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
state_ = 2;
|
||||
// [[fallthrough]]
|
||||
|
||||
do_read:
|
||||
return async_read_some(
|
||||
s_, b_, p_, std::move(*this));
|
||||
|
||||
case 1:
|
||||
goto upcall;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
b_.consume(bytes_used);
|
||||
if(ec)
|
||||
return 0;
|
||||
BOOST_ASSERT(parser.is_complete());
|
||||
goto upcall;
|
||||
if(Condition{}(p_))
|
||||
goto upcall;
|
||||
state_ = 3;
|
||||
goto do_read;
|
||||
}
|
||||
else if(! ec)
|
||||
{
|
||||
parser.commit_body(bytes_transferred);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
upcall:
|
||||
h_(ec);
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
inline
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, true, Derived>& parser,
|
||||
error_code& ec)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Handler>
|
||||
class read_msg_op
|
||||
{
|
||||
switch(parser.state())
|
||||
using parser_type =
|
||||
message_parser<isRequest, Body, Fields>;
|
||||
|
||||
using message_type =
|
||||
message<isRequest, Body, Fields>;
|
||||
|
||||
struct data
|
||||
{
|
||||
case parse_state::header:
|
||||
case parse_state::chunk_header:
|
||||
return detail::read_some_buffer(
|
||||
stream, buffer, parser, ec);
|
||||
int state = 0;
|
||||
Stream& s;
|
||||
DynamicBuffer& b;
|
||||
message_type& m;
|
||||
parser_type p;
|
||||
|
||||
default:
|
||||
return detail::read_some_body(
|
||||
stream, buffer, parser, ec);
|
||||
data(Handler& handler, Stream& s_,
|
||||
DynamicBuffer& b_, message_type& m_)
|
||||
: s(s_)
|
||||
, b(b_)
|
||||
, m(m_)
|
||||
{
|
||||
p.eager(true);
|
||||
}
|
||||
};
|
||||
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
read_msg_op(read_msg_op&&) = default;
|
||||
read_msg_op(read_msg_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
read_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
inline
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, false, Derived>& parser,
|
||||
error_code& ec)
|
||||
void
|
||||
operator()(error_code ec, std::size_t bytes_used);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->d_->state >= 2 ? true :
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, read_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Handler>
|
||||
void
|
||||
read_msg_op<Stream, DynamicBuffer,
|
||||
isRequest, Body, Fields, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_used)
|
||||
{
|
||||
return detail::read_some_buffer(
|
||||
stream, buffer, parser, ec);
|
||||
auto& d = *d_;
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
|
||||
do_read:
|
||||
return async_read_some(
|
||||
d.s, d.b, d.p, std::move(*this));
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
d.b.consume(bytes_used);
|
||||
if(ec)
|
||||
goto upcall;
|
||||
if(d.p.is_done())
|
||||
{
|
||||
d.m = d.p.release();
|
||||
goto upcall;
|
||||
}
|
||||
d.state = 2;
|
||||
goto do_read;
|
||||
}
|
||||
upcall:
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
} // detail
|
||||
@ -190,18 +427,18 @@ read_some(
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser)
|
||||
basic_parser<isRequest, Derived>& parser)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
BOOST_ASSERT(! parser.is_done());
|
||||
error_code ec;
|
||||
auto const bytes_used =
|
||||
read_some(stream, buffer, parser, ec);
|
||||
@ -213,37 +450,190 @@ read_some(
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
return detail::read_some(stream, buffer, parser, ec);
|
||||
BOOST_ASSERT(! parser.is_done());
|
||||
std::size_t bytes_used = 0;
|
||||
if(buffer.size() == 0)
|
||||
goto do_read;
|
||||
for(;;)
|
||||
{
|
||||
// invoke parser
|
||||
bytes_used += parser.put(buffer.data(), ec);
|
||||
if(! ec)
|
||||
break;
|
||||
if(ec != http::error::need_more)
|
||||
break;
|
||||
ec = {};
|
||||
do_read:
|
||||
boost::optional<typename
|
||||
DynamicBuffer::mutable_buffers_type> b;
|
||||
try
|
||||
{
|
||||
using beast::detail::read_size_helper;
|
||||
b.emplace(buffer.prepare(
|
||||
read_size_helper(buffer, 65536)));
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
break;
|
||||
}
|
||||
auto const bytes_transferred =
|
||||
stream.read_some(*b, ec);
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
if(parser.got_some())
|
||||
{
|
||||
// caller sees EOF on next read
|
||||
ec = {};
|
||||
parser.put_eof(ec);
|
||||
if(ec)
|
||||
break;
|
||||
BOOST_ASSERT(parser.is_done());
|
||||
break;
|
||||
}
|
||||
ec = error::end_of_stream;
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
break;
|
||||
buffer.commit(bytes_transferred);
|
||||
}
|
||||
return bytes_used;
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code, std::size_t)>
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_done());
|
||||
async_completion<ReadHandler,
|
||||
void(error_code, std::size_t)> init{handler};
|
||||
detail::read_some_op<AsyncReadStream,
|
||||
DynamicBuffer, isRequest, Derived, handler_type<
|
||||
ReadHandler, void(error_code, std::size_t)>>{
|
||||
init.completion_handler, stream, buffer, parser}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser)
|
||||
basic_parser<isRequest, Derived>& parser)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
error_code ec;
|
||||
read_header(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(false);
|
||||
while(! parser.is_header_done())
|
||||
{
|
||||
auto const bytes_used =
|
||||
read_some(stream, buffer, parser, ec);
|
||||
buffer.consume(bytes_used);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code)>
|
||||
async_read_header(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(false);
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::read_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Derived, detail::parser_is_header_done,
|
||||
handler_type<ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, parser}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
error_code ec;
|
||||
read(stream, buffer, parser, ec);
|
||||
if(ec)
|
||||
@ -253,30 +643,59 @@ read(
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
BOOST_ASSERT(! parser.is_complete());
|
||||
do
|
||||
parser.eager(true);
|
||||
while(! parser.is_done())
|
||||
{
|
||||
auto const bytes_used =
|
||||
read_some(stream, buffer, parser, ec);
|
||||
auto const bytes_used = read_some(
|
||||
stream, buffer, parser, ec);
|
||||
buffer.consume(bytes_used);
|
||||
if(ec)
|
||||
return;
|
||||
buffer.consume(bytes_used);
|
||||
}
|
||||
while(! parser.is_complete());
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code)>
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
parser.eager(true);
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::read_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Derived, detail::parser_is_done,
|
||||
handler_type<ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, parser}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
@ -296,7 +715,7 @@ read(
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
error_code ec;
|
||||
beast::http::read(stream, buffer, msg, ec);
|
||||
read(stream, buffer, msg, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
@ -321,12 +740,44 @@ read(
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
message_parser<isRequest, Body, Fields> p;
|
||||
beast::http::read(stream, buffer, p, ec);
|
||||
p.eager(true);
|
||||
read(stream, buffer, p.base(), ec);
|
||||
if(ec)
|
||||
return;
|
||||
msg = p.release();
|
||||
}
|
||||
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class ReadHandler>
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code)>
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
message<isRequest, Body, Fields>& msg,
|
||||
ReadHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"AsyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
async_completion<ReadHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::read_msg_op<AsyncReadStream, DynamicBuffer,
|
||||
isRequest, Body, Fields, handler_type<
|
||||
ReadHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, buffer, msg}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
|
@ -81,19 +81,7 @@ serializer(message<isRequest, Body, Fields> const& m,
|
||||
: m_(m)
|
||||
, d_(d)
|
||||
, b_(1024, alloc)
|
||||
, chunked_(token_list{
|
||||
m.fields["Transfer-Encoding"]}.exists("chunked"))
|
||||
, close_(token_list{
|
||||
m.fields["Connection"]}.exists("close") ||
|
||||
(m.version < 11 && ! m.fields.exists(
|
||||
"Content-Length")))
|
||||
{
|
||||
s_ = chunked_ ? do_init_c : do_init;
|
||||
// VFALCO Move this stuff to the switch?
|
||||
auto os = ostream(b_);
|
||||
detail::write_start_line(os, m_);
|
||||
detail::write_fields(os, m_.fields);
|
||||
os << "\r\n";
|
||||
}
|
||||
|
||||
template<bool isRequest, class Body, class Fields,
|
||||
@ -107,6 +95,22 @@ get(error_code& ec, Visit&& visit)
|
||||
using boost::asio::buffer_size;
|
||||
switch(s_)
|
||||
{
|
||||
case do_construct:
|
||||
{
|
||||
chunked_ = token_list{
|
||||
m_.fields["Transfer-Encoding"]}.exists("chunked");
|
||||
close_ = token_list{m_.fields["Connection"]}.exists("close") ||
|
||||
(m_.version < 11 && ! m_.fields.exists("Content-Length"));
|
||||
auto os = ostream(b_);
|
||||
detail::write_start_line(os, m_);
|
||||
detail::write_fields(os, m_.fields);
|
||||
os << "\r\n";
|
||||
if(chunked_)
|
||||
goto go_init_c;
|
||||
s_ = do_init;
|
||||
// [[fallthrough]]
|
||||
}
|
||||
|
||||
case do_init:
|
||||
{
|
||||
if(split_)
|
||||
@ -171,6 +175,8 @@ get(error_code& ec, Visit&& visit)
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
go_init_c:
|
||||
s_ = do_init_c;
|
||||
case do_init_c:
|
||||
{
|
||||
if(split_)
|
||||
@ -320,7 +326,7 @@ consume(std::size_t n)
|
||||
break;
|
||||
// VFALCO delete b_?
|
||||
header_done_ = true;
|
||||
if(! is_deferred::value)
|
||||
if(! split_)
|
||||
goto go_complete;
|
||||
s_ = do_body;
|
||||
break;
|
||||
@ -364,7 +370,7 @@ consume(std::size_t n)
|
||||
break;
|
||||
// VFALCO delete b_?
|
||||
header_done_ = true;
|
||||
if(! is_deferred::value)
|
||||
if(! split_)
|
||||
{
|
||||
s_ = do_final_c;
|
||||
break;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <ostream>
|
||||
@ -27,12 +28,14 @@ namespace beast {
|
||||
namespace http {
|
||||
namespace detail {
|
||||
|
||||
template<
|
||||
class Stream, class Serializer, class Handler>
|
||||
template<class Stream, bool isRequest, class Body,
|
||||
class Fields, class Decorator, class Allocator,
|
||||
class Handler>
|
||||
class write_some_op
|
||||
{
|
||||
Stream& s_;
|
||||
Serializer& sr_;
|
||||
serializer<isRequest, Body, Fields,
|
||||
Decorator, Allocator>& sr_;
|
||||
Handler h_;
|
||||
|
||||
class lambda
|
||||
@ -40,7 +43,7 @@ class write_some_op
|
||||
write_some_op& op_;
|
||||
|
||||
public:
|
||||
bool empty = true;
|
||||
bool invoked = false;
|
||||
|
||||
explicit
|
||||
lambda(write_some_op& op)
|
||||
@ -53,7 +56,7 @@ class write_some_op
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffer)
|
||||
{
|
||||
empty = false;
|
||||
invoked = true;
|
||||
return op_.s_.async_write_some(
|
||||
buffer, std::move(op_));
|
||||
}
|
||||
@ -65,7 +68,8 @@ public:
|
||||
|
||||
template<class DeducedHandler>
|
||||
write_some_op(DeducedHandler&& h,
|
||||
Stream& s, Serializer& sr)
|
||||
Stream& s, serializer<isRequest, Body,
|
||||
Fields, Decorator, Allocator>& sr)
|
||||
: s_(s)
|
||||
, sr_(sr)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
@ -114,25 +118,40 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template<
|
||||
class Stream, class Serializer, class Handler>
|
||||
template<class Stream, bool isRequest, class Body,
|
||||
class Fields, class Decorator, class Allocator,
|
||||
class Handler>
|
||||
void
|
||||
write_some_op<Stream, Serializer, Handler>::
|
||||
write_some_op<Stream, isRequest, Body,
|
||||
Fields, Decorator, Allocator, Handler>::
|
||||
operator()()
|
||||
{
|
||||
lambda f{*this};
|
||||
error_code ec;
|
||||
if(sr_.is_done())
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
lambda f{*this};
|
||||
sr_.get(ec, f);
|
||||
if(! ec && ! f.empty)
|
||||
if(ec)
|
||||
{
|
||||
BOOST_ASSERT(! f.invoked);
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
if(f.invoked)
|
||||
// *this has been moved from,
|
||||
// cannot access members here.
|
||||
return;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
|
||||
template<
|
||||
class Stream, class Serializer, class Handler>
|
||||
template<class Stream, bool isRequest, class Body,
|
||||
class Fields, class Decorator, class Allocator,
|
||||
class Handler>
|
||||
void
|
||||
write_some_op<Stream, Serializer, Handler>::
|
||||
write_some_op<Stream, isRequest, Body,
|
||||
Fields, Decorator, Allocator, Handler>::
|
||||
operator()(error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
if(! ec)
|
||||
@ -142,13 +161,199 @@ operator()(error_code ec, std::size_t bytes_transferred)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct serializer_is_header_done
|
||||
{
|
||||
template<bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
bool
|
||||
operator()(serializer<isRequest, Body, Fields,
|
||||
Decorator, Allocator> const& sr) const
|
||||
{
|
||||
return sr.is_header_done();
|
||||
}
|
||||
};
|
||||
|
||||
struct serializer_is_done
|
||||
{
|
||||
template<bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
bool
|
||||
operator()(serializer<isRequest, Body, Fields,
|
||||
Decorator, Allocator> const& sr) const
|
||||
{
|
||||
return sr.is_done();
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, bool isRequest, class Body,
|
||||
class Fields, class Decorator, class Allocator,
|
||||
class Predicate, class Handler>
|
||||
class write_op
|
||||
{
|
||||
int state_ = 0;
|
||||
Stream& s_;
|
||||
serializer<isRequest, Body, Fields,
|
||||
Decorator, Allocator>& sr_;
|
||||
Handler h_;
|
||||
|
||||
class lambda
|
||||
{
|
||||
write_op& op_;
|
||||
|
||||
public:
|
||||
bool invoked = false;
|
||||
|
||||
explicit
|
||||
lambda(write_op& op)
|
||||
: op_(op)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffer)
|
||||
{
|
||||
invoked = true;
|
||||
return op_.s_.async_write_some(
|
||||
buffer, std::move(op_));
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
|
||||
template<class DeducedHandler>
|
||||
write_op(DeducedHandler&& h, Stream& s,
|
||||
serializer<isRequest, Body, Fields,
|
||||
Decorator, Allocator>& sr)
|
||||
: s_(s)
|
||||
, sr_(sr)
|
||||
, h_(std::forward<DeducedHandler>(h))
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->h_));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->state_ >= 3 ||
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->h_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->h_));
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream, bool isRequest, class Body,
|
||||
class Fields, class Decorator, class Allocator,
|
||||
class Predicate, class Handler>
|
||||
void
|
||||
write_op<Stream, isRequest, Body, Fields,
|
||||
Decorator, Allocator, Predicate, Handler>::
|
||||
operator()(error_code ec,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
if(ec)
|
||||
goto upcall;
|
||||
switch(state_)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if(Predicate{}(sr_))
|
||||
{
|
||||
state_ = 1;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
lambda f{*this};
|
||||
state_ = 2;
|
||||
sr_.get(ec, f);
|
||||
if(ec)
|
||||
{
|
||||
BOOST_ASSERT(! f.invoked);
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
if(f.invoked)
|
||||
// *this has been moved from,
|
||||
// cannot access members here.
|
||||
return;
|
||||
BOOST_ASSERT(Predicate{}(sr_));
|
||||
state_ = 1;
|
||||
return s_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
|
||||
case 1:
|
||||
goto upcall;
|
||||
|
||||
case 2:
|
||||
state_ = 3;
|
||||
// [[fallthrough]]
|
||||
|
||||
case 3:
|
||||
{
|
||||
sr_.consume(bytes_transferred);
|
||||
if(Predicate{}(sr_))
|
||||
goto upcall;
|
||||
lambda f{*this};
|
||||
sr_.get(ec, f);
|
||||
if(ec)
|
||||
{
|
||||
BOOST_ASSERT(! f.invoked);
|
||||
goto upcall;
|
||||
}
|
||||
if(f.invoked)
|
||||
// *this has been moved from,
|
||||
// cannot access members here.
|
||||
return;
|
||||
BOOST_ASSERT(Predicate{}(sr_));
|
||||
goto upcall;
|
||||
}
|
||||
}
|
||||
upcall:
|
||||
h_(ec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
class write_op
|
||||
class write_msg_op
|
||||
{
|
||||
struct data
|
||||
{
|
||||
int state = 0;
|
||||
Stream& s;
|
||||
serializer<isRequest, Body, Fields,
|
||||
empty_decorator, handler_alloc<char, Handler>> sr;
|
||||
@ -165,22 +370,25 @@ class write_op
|
||||
handler_ptr<data, Handler> d_;
|
||||
|
||||
public:
|
||||
write_op(write_op&&) = default;
|
||||
write_op(write_op const&) = default;
|
||||
write_msg_op(write_msg_op&&) = default;
|
||||
write_msg_op(write_msg_op const&) = default;
|
||||
|
||||
template<class DeducedHandler, class... Args>
|
||||
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
write_msg_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||
: d_(std::forward<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
operator()();
|
||||
|
||||
void
|
||||
operator()(error_code ec);
|
||||
|
||||
friend
|
||||
void* asio_handler_allocate(
|
||||
std::size_t size, write_op* op)
|
||||
std::size_t size, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
@ -189,7 +397,7 @@ public:
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_op* op)
|
||||
void* p, std::size_t size, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
@ -197,17 +405,16 @@ public:
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_op* op)
|
||||
bool asio_handler_is_continuation(write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return op->d_->state == 2 ||
|
||||
asio_handler_is_continuation(
|
||||
std::addressof(op->d_.handler()));
|
||||
return asio_handler_is_continuation(
|
||||
std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_op* op)
|
||||
void asio_handler_invoke(Function&& f, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
@ -218,35 +425,23 @@ public:
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_op<Stream, Handler, isRequest, Body, Fields>::
|
||||
write_msg_op<Stream, Handler, isRequest, Body, Fields>::
|
||||
operator()()
|
||||
{
|
||||
auto& d = *d_;
|
||||
return async_write(d.s, d.sr, std::move(*this));
|
||||
}
|
||||
|
||||
template<class Stream, class Handler,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write_msg_op<Stream, Handler, isRequest, Body, Fields>::
|
||||
operator()(error_code ec)
|
||||
{
|
||||
auto& d = *d_;
|
||||
if(ec)
|
||||
goto upcall;
|
||||
switch(d.state)
|
||||
{
|
||||
case 0:
|
||||
d.state = 1;
|
||||
write_some_op<Stream, decltype(d.sr),
|
||||
write_op>{std::move(*this), d.s, d.sr}();
|
||||
return;
|
||||
case 1:
|
||||
d.state = 2;
|
||||
// [[fallthrough]]
|
||||
case 2:
|
||||
if(d.sr.is_done())
|
||||
{
|
||||
if(d.sr.needs_close())
|
||||
// VFALCO Choose an error code
|
||||
ec = boost::asio::error::eof;
|
||||
break;
|
||||
}
|
||||
write_some_op<Stream, decltype(d.sr),
|
||||
write_op>{std::move(*this), d.s, d.sr}();
|
||||
return;
|
||||
}
|
||||
upcall:
|
||||
if(! ec)
|
||||
if(d.sr.need_close())
|
||||
ec = error::end_of_stream;
|
||||
d_.invoke(ec);
|
||||
}
|
||||
|
||||
@ -258,7 +453,8 @@ class write_some_lambda
|
||||
Stream& stream_;
|
||||
|
||||
public:
|
||||
boost::optional<std::size_t> bytes_transferred;
|
||||
bool invoked = false;
|
||||
std::size_t bytes_transferred = 0;
|
||||
|
||||
explicit
|
||||
write_some_lambda(Stream& stream)
|
||||
@ -271,25 +467,55 @@ public:
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffer)
|
||||
{
|
||||
invoked = true;
|
||||
bytes_transferred =
|
||||
stream_.write_some(buffer, ec);
|
||||
}
|
||||
};
|
||||
|
||||
template<class Stream>
|
||||
class write_lambda
|
||||
{
|
||||
Stream& stream_;
|
||||
|
||||
public:
|
||||
bool invoked = false;
|
||||
std::size_t bytes_transferred = 0;
|
||||
|
||||
explicit
|
||||
write_lambda(Stream& stream)
|
||||
: stream_(stream)
|
||||
{
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
operator()(error_code& ec,
|
||||
ConstBufferSequence const& buffer)
|
||||
{
|
||||
invoked = true;
|
||||
bytes_transferred = boost::asio::write(
|
||||
stream_, buffer, ec);
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
template<class SyncWriteStream, bool isRequest,
|
||||
class Body, class Fields, class Decorator,
|
||||
class Allocator>
|
||||
void
|
||||
write_some(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
error_code ec;
|
||||
write_some(stream, sr, ec);
|
||||
if(ec)
|
||||
@ -307,15 +533,18 @@ write_some(SyncWriteStream& stream, serializer<
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
detail::write_some_lambda<SyncWriteStream> f{stream};
|
||||
sr.get(ec, f);
|
||||
if(! ec)
|
||||
if(! sr.is_done())
|
||||
{
|
||||
if(f.bytes_transferred)
|
||||
sr.consume(*f.bytes_transferred);
|
||||
if(sr.is_done() && sr.needs_close())
|
||||
// VFALCO decide on an error code
|
||||
ec = boost::asio::error::eof;
|
||||
sr.get(ec, f);
|
||||
if(ec)
|
||||
return;
|
||||
if(f.invoked)
|
||||
sr.consume(f.bytes_transferred);
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,12 +565,159 @@ async_write_some(AsyncWriteStream& stream, serializer<
|
||||
"BodyReader requirements not met");
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_some_op<AsyncWriteStream, decltype(sr),
|
||||
handler_type<WriteHandler, void(error_code)>>{
|
||||
detail::write_some_op<AsyncWriteStream, isRequest,
|
||||
Body, Fields, Decorator, Allocator, handler_type<
|
||||
WriteHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, sr}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write_header(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
error_code ec;
|
||||
write_header(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write_header(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
sr.split(true);
|
||||
detail::write_lambda<SyncWriteStream> f{stream};
|
||||
while(! sr.is_header_done())
|
||||
{
|
||||
sr.get(ec, f);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(f.invoked);
|
||||
sr.consume(f.bytes_transferred);
|
||||
}
|
||||
}
|
||||
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator, class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write_header(AsyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
sr.split(true);
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_op<AsyncWriteStream, isRequest, Body, Fields,
|
||||
Decorator, Allocator, detail::serializer_is_header_done,
|
||||
handler_type<WriteHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, sr}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
error_code ec;
|
||||
write(stream, sr, ec);
|
||||
if(ec)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
}
|
||||
|
||||
template<
|
||||
class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
error_code& ec)
|
||||
{
|
||||
static_assert(is_sync_write_stream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
sr.split(false);
|
||||
detail::write_lambda<SyncWriteStream> f{stream};
|
||||
while(! sr.is_done())
|
||||
{
|
||||
sr.get(ec, f);
|
||||
if(ec)
|
||||
return;
|
||||
if(f.invoked)
|
||||
sr.consume(f.bytes_transferred);
|
||||
}
|
||||
if(sr.need_close())
|
||||
ec = error::end_of_stream;
|
||||
}
|
||||
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator, class WriteHandler>
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
async_write(AsyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(is_async_write_stream<
|
||||
AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
sr.split(false);
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_op<AsyncWriteStream, isRequest, Body,
|
||||
Fields, Decorator, Allocator, detail::serializer_is_done,
|
||||
handler_type<WriteHandler, void(error_code)>>{
|
||||
init.completion_handler, stream, sr}(
|
||||
error_code{}, 0);
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
@ -374,18 +750,10 @@ write(SyncWriteStream& stream,
|
||||
static_assert(is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
auto sr = make_serializer(msg);
|
||||
for(;;)
|
||||
{
|
||||
#if 0
|
||||
sr.write_some(stream, ec);
|
||||
#else
|
||||
write_some(stream, sr, ec);
|
||||
#endif
|
||||
if(ec)
|
||||
return;
|
||||
if(sr.is_done())
|
||||
break;
|
||||
}
|
||||
write(stream, sr, ec);
|
||||
if(! ec)
|
||||
if(sr.need_close())
|
||||
ec = error::end_of_stream;
|
||||
}
|
||||
|
||||
template<class AsyncWriteStream,
|
||||
@ -406,11 +774,10 @@ async_write(AsyncWriteStream& stream,
|
||||
"BodyReader requirements not met");
|
||||
async_completion<WriteHandler,
|
||||
void(error_code)> init{handler};
|
||||
detail::write_op<AsyncWriteStream, handler_type<
|
||||
detail::write_msg_op<AsyncWriteStream, handler_type<
|
||||
WriteHandler, void(error_code)>,
|
||||
isRequest, Body, Fields>{
|
||||
init.completion_handler, stream, msg}(
|
||||
error_code{});
|
||||
init.completion_handler, stream, msg}();
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
@ -439,7 +806,7 @@ operator<<(std::ostream& os,
|
||||
beast::detail::sync_ostream oss{os};
|
||||
error_code ec;
|
||||
write(oss, msg, ec);
|
||||
if(ec && ec != boost::asio::error::eof)
|
||||
if(ec && ec != error::end_of_stream)
|
||||
BOOST_THROW_EXCEPTION(system_error{ec});
|
||||
return os;
|
||||
}
|
||||
|
@ -34,13 +34,18 @@ namespace http {
|
||||
|
||||
@note A new instance of the parser is required for each message.
|
||||
*/
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
template<bool isRequest, class Body, class Fields = fields>
|
||||
class message_parser
|
||||
: public basic_parser<isRequest,
|
||||
Body::writer::is_direct,
|
||||
message_parser<isRequest, Body, Fields>>
|
||||
message_parser<isRequest, Body, Fields>>
|
||||
{
|
||||
using base_type = basic_parser<isRequest, true,
|
||||
static_assert(is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
|
||||
static_assert(is_body_writer<Body>::value,
|
||||
"BodyWriter requirements not met");
|
||||
|
||||
using base_type = basic_parser<isRequest,
|
||||
message_parser<isRequest, Body, Fields>>;
|
||||
|
||||
using writer_type = typename Body::writer;
|
||||
@ -52,10 +57,6 @@ public:
|
||||
/// The type of message returned by the parser
|
||||
using value_type = message<isRequest, Body, Fields>;
|
||||
|
||||
/// The type of buffer sequence representing the body
|
||||
using mutable_buffers_type =
|
||||
typename writer_type::mutable_buffers_type;
|
||||
|
||||
/// Constructor (default)
|
||||
message_parser() = default;
|
||||
|
||||
@ -99,9 +100,7 @@ public:
|
||||
#endif
|
||||
|
||||
/** Construct a message parser from a @ref header_parser.
|
||||
|
||||
@param parser The header parser to construct from.
|
||||
|
||||
@param args Optional arguments forwarded to the message
|
||||
constructor.
|
||||
*/
|
||||
@ -152,8 +151,7 @@ public:
|
||||
|
||||
private:
|
||||
friend class basic_parser<
|
||||
isRequest, Body::writer::is_direct,
|
||||
message_parser>;
|
||||
isRequest, message_parser>;
|
||||
|
||||
void
|
||||
on_request(
|
||||
@ -190,61 +188,25 @@ private:
|
||||
}
|
||||
|
||||
void
|
||||
on_body()
|
||||
{
|
||||
wr_.emplace(m_);
|
||||
wr_->init();
|
||||
}
|
||||
|
||||
void
|
||||
on_body(std::uint64_t content_length)
|
||||
{
|
||||
wr_.emplace(m_);
|
||||
wr_->init(content_length);
|
||||
}
|
||||
|
||||
void
|
||||
on_body(error_code& ec)
|
||||
{
|
||||
wr_.emplace(m_);
|
||||
wr_->init(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
on_body(std::uint64_t content_length,
|
||||
error_code& ec)
|
||||
on_body(boost::optional<
|
||||
std::uint64_t> const& content_length,
|
||||
error_code& ec)
|
||||
{
|
||||
wr_.emplace(m_);
|
||||
wr_->init(content_length, ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
on_data(string_view const& s,
|
||||
error_code& ec)
|
||||
{
|
||||
BOOST_STATIC_ASSERT(! Body::writer::is_direct);
|
||||
wr_->write(s, ec);
|
||||
}
|
||||
|
||||
mutable_buffers_type
|
||||
on_prepare(std::size_t n)
|
||||
{
|
||||
return wr_->prepare(n);
|
||||
wr_->put(boost::asio::buffer(
|
||||
s.data(), s.size()), ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_commit(std::size_t n)
|
||||
{
|
||||
wr_->commit(n);
|
||||
}
|
||||
|
||||
void
|
||||
on_chunk(std::uint64_t,
|
||||
string_view const&,
|
||||
on_chunk(
|
||||
std::uint64_t, string_view const&,
|
||||
error_code&)
|
||||
{
|
||||
}
|
||||
@ -253,28 +215,18 @@ private:
|
||||
on_complete(error_code& ec)
|
||||
{
|
||||
if(wr_)
|
||||
do_on_complete(ec,
|
||||
std::integral_constant<bool,
|
||||
Body::writer::is_direct>{});
|
||||
}
|
||||
|
||||
void
|
||||
do_on_complete(
|
||||
error_code& ec, std::true_type)
|
||||
{
|
||||
wr_->finish();
|
||||
}
|
||||
|
||||
void
|
||||
do_on_complete(
|
||||
error_code& ec, std::false_type)
|
||||
{
|
||||
wr_->finish(ec);
|
||||
if(ec)
|
||||
return;
|
||||
wr_->finish(ec);
|
||||
}
|
||||
};
|
||||
|
||||
/// A parser for producing HTTP/1 messages
|
||||
template<class Body, class Fields = fields>
|
||||
using request_parser = message_parser<true, Body, Fields>;
|
||||
|
||||
/// A parser for producing HTTP/1 messages
|
||||
template<class Body, class Fields = fields>
|
||||
using response_parser = message_parser<false, Body, Fields>;
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
|
@ -66,12 +66,12 @@ namespace http {
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser);
|
||||
basic_parser<isRequest, Derived>& parser);
|
||||
|
||||
/** Read some HTTP/1 message data from a stream.
|
||||
|
||||
@ -122,12 +122,12 @@ read_some(
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
std::size_t
|
||||
read_some(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Start an asynchronous operation to read some HTTP/1 message data from a stream.
|
||||
@ -192,7 +192,7 @@ read_some(
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
@ -203,7 +203,170 @@ async_return_type<
|
||||
async_read_some(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read a header into an HTTP/1 parser from a stream.
|
||||
|
||||
This function synchronously reads from a stream and passes
|
||||
data to the specified parser. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li The parser indicates that the complete header is received
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function is implemented in terms of one or more calls
|
||||
to the stream's `read_some` function. The implementation may
|
||||
read additional octets that lie past the end of the object
|
||||
being parsed. This additional data is stored in the dynamic
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
If the end of the stream is reached during the read, the
|
||||
value @ref error::partial_message is indicated as the
|
||||
error if bytes have been processed, else the error
|
||||
@ref error::end_of_stream is indicated.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `false` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser);
|
||||
|
||||
/** Read a header into an HTTP/1 parser from a stream.
|
||||
|
||||
This function synchronously reads from a stream and passes
|
||||
data to the specified parser. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li The parser indicates that the complete header is received
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This function is implemented in terms of one or more calls
|
||||
to the stream's `read_some` function. The implementation may
|
||||
read additional octets that lie past the end of the object
|
||||
being parsed. This additional data is stored in the dynamic
|
||||
buffer, which may be used in subsequent calls.
|
||||
|
||||
If the end of the stream is reached during the read, the
|
||||
value @ref error::partial_message is indicated as the
|
||||
error if bytes have been processed, else the error
|
||||
@ref error::end_of_stream is indicated.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b SyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `false` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read_header(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Read a header into an HTTP/1 parser asynchronously from a stream.
|
||||
|
||||
This function is used to asynchronously read from a stream and
|
||||
pass the data to the specified parser. The function call always
|
||||
returns immediately. The asynchronous operation will continue
|
||||
until one of the following conditions is true:
|
||||
|
||||
@li The parser indicates that the complete header is received
|
||||
|
||||
@li An error occurs in the stream or parser.
|
||||
|
||||
This operation is implemented in terms of one or more calls to
|
||||
the next layer's `async_read_some` function, and is known as a
|
||||
<em>composed operation</em>. The program must ensure that the
|
||||
stream performs no other operations until this operation completes.
|
||||
The implementation may read additional octets that lie past the
|
||||
end of the object being parsed. This additional data is stored
|
||||
in the stream buffer, which may be used in subsequent calls.
|
||||
|
||||
If the end of the stream is reached during the read, the
|
||||
value @ref error::partial_message is indicated as the
|
||||
error if bytes have been processed, else the error
|
||||
@ref error::end_of_stream is indicated.
|
||||
|
||||
@param stream The stream from which the data is to be read.
|
||||
The type must support the @b AsyncReadStream concept.
|
||||
|
||||
@param buffer A @b DynamicBuffer holding additional bytes
|
||||
read by the implementation from the stream. This is both
|
||||
an input and an output parameter; on entry, any data in the
|
||||
dynamic buffer's input sequence will be given to the parser
|
||||
first.
|
||||
|
||||
@param parser The parser to use.
|
||||
|
||||
@param handler The handler to be called when the request
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `false` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
async_return_type<
|
||||
ReadHandler, void(error_code)>
|
||||
#endif
|
||||
async_read_header(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -241,16 +404,19 @@ async_read_some(
|
||||
@param parser The parser to use.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `true` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser);
|
||||
basic_parser<isRequest, Derived>& parser);
|
||||
|
||||
/** Read into an HTTP/1 parser from a stream.
|
||||
|
||||
@ -285,16 +451,19 @@ read(
|
||||
@param parser The parser to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `true` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
void
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec);
|
||||
|
||||
/** Read into an HTTP/1 parser asynchronously from a stream.
|
||||
@ -342,11 +511,14 @@ read(
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
@note The implementation will call @ref basic_parser::eager
|
||||
with the value `true` on the parser passed in.
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
bool isRequest, bool isDirect, class Derived,
|
||||
bool isRequest, class Derived,
|
||||
class ReadHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
@ -357,9 +529,11 @@ async_return_type<
|
||||
async_read(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
ReadHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Read an HTTP/1 message from a stream.
|
||||
|
||||
This function is used to synchronously read a message from
|
||||
@ -522,7 +696,6 @@ async_read(
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#include <beast/http/impl/async_read.ipp>
|
||||
#include <beast/http/impl/read.ipp>
|
||||
|
||||
#endif
|
||||
|
@ -65,7 +65,7 @@ struct empty_decorator
|
||||
if the contents of the message indicate that chunk encoding
|
||||
is required. If the semantics of the message indicate that
|
||||
the connection should be closed after the message is sent, the
|
||||
function @ref needs_close will return `true`.
|
||||
function @ref need_close will return `true`.
|
||||
|
||||
Upon construction, an optional chunk decorator may be
|
||||
specified. This decorator is a function object called with
|
||||
@ -135,9 +135,11 @@ class serializer
|
||||
|
||||
enum
|
||||
{
|
||||
do_init = 0,
|
||||
do_header_only = 10,
|
||||
do_header = 20,
|
||||
do_construct = 0,
|
||||
|
||||
do_init = 10,
|
||||
do_header_only = 20,
|
||||
do_header = 30,
|
||||
do_body = 40,
|
||||
|
||||
do_init_c = 50,
|
||||
@ -191,7 +193,7 @@ class serializer
|
||||
buffer_type b_;
|
||||
boost::variant<boost::blank,
|
||||
cb0_t, cb1_t, ch0_t, ch1_t, ch2_t> v_;
|
||||
int s_;
|
||||
int s_ = do_construct;
|
||||
bool split_ = is_deferred::value;
|
||||
bool header_done_ = false;
|
||||
bool chunked_;
|
||||
@ -201,8 +203,13 @@ class serializer
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
@param msg The message to serialize, which must
|
||||
remain valid for the lifetime of the serializer.
|
||||
The implementation guarantees that the message passed on
|
||||
construction will not be accessed until the first call to
|
||||
@ref get. This allows the message to be lazily created.
|
||||
For example, if the header is filled in before serialization.
|
||||
|
||||
@param msg The message to serialize, which must remain valid
|
||||
for the lifetime of the serializer.
|
||||
|
||||
@param decorator An optional decorator to use.
|
||||
|
||||
@ -268,7 +275,7 @@ public:
|
||||
function returns `true`.
|
||||
*/
|
||||
bool
|
||||
needs_close() const
|
||||
need_close() const
|
||||
{
|
||||
return close_;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
#define BEAST_HTTP_STRING_BODY_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
@ -30,68 +30,6 @@ struct string_body
|
||||
/// The type of the body member when used in a message.
|
||||
using value_type = std::string;
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm used store buffers in this body
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
std::size_t len_ = 0;
|
||||
|
||||
public:
|
||||
static bool constexpr is_direct = true;
|
||||
|
||||
using mutable_buffers_type =
|
||||
boost::asio::mutable_buffers_1;
|
||||
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest,
|
||||
string_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(std::uint64_t content_length)
|
||||
{
|
||||
if(content_length >
|
||||
(std::numeric_limits<std::size_t>::max)())
|
||||
BOOST_THROW_EXCEPTION(std::length_error{
|
||||
"Content-Length overflow"});
|
||||
body_.reserve(static_cast<
|
||||
std::size_t>(content_length));
|
||||
}
|
||||
|
||||
mutable_buffers_type
|
||||
prepare(std::size_t n)
|
||||
{
|
||||
body_.resize(len_ + n);
|
||||
return {&body_[len_], n};
|
||||
}
|
||||
|
||||
void
|
||||
commit(std::size_t n)
|
||||
{
|
||||
if(body_.size() > len_ + n)
|
||||
body_.resize(len_ + n);
|
||||
len_ = body_.size();
|
||||
}
|
||||
|
||||
void
|
||||
finish()
|
||||
{
|
||||
body_.resize(len_);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm to obtain buffers representing the body
|
||||
using reader = implementation_defined;
|
||||
@ -134,6 +72,69 @@ struct string_body
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#if BEAST_DOXYGEN
|
||||
/// The algorithm used store buffers in this body
|
||||
using writer = implementation_defined;
|
||||
#else
|
||||
class writer
|
||||
{
|
||||
value_type& body_;
|
||||
|
||||
public:
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(message<isRequest, string_body, Fields>& m)
|
||||
: body_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
init(boost::optional<
|
||||
std::uint64_t> content_length, error_code& ec)
|
||||
{
|
||||
if(content_length)
|
||||
{
|
||||
if(*content_length > (std::numeric_limits<
|
||||
std::size_t>::max)())
|
||||
{
|
||||
ec = make_error_code(
|
||||
errc::not_enough_memory);
|
||||
return;
|
||||
}
|
||||
body_.reserve(static_cast<
|
||||
std::size_t>(*content_length));
|
||||
}
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
put(ConstBufferSequence const& buffers,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
using boost::asio::buffer_copy;
|
||||
auto const n = buffer_size(buffers);
|
||||
auto const len = body_.size();
|
||||
try
|
||||
{
|
||||
body_.resize(len + n);
|
||||
}
|
||||
catch(std::length_error const&)
|
||||
{
|
||||
ec = error::buffer_overflow;
|
||||
return;
|
||||
}
|
||||
buffer_copy(boost::asio::buffer(
|
||||
&body_[0] + len, n), buffers);
|
||||
}
|
||||
|
||||
void
|
||||
finish(error_code&)
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // http
|
||||
|
@ -114,28 +114,24 @@ struct is_body_reader<T, beast::detail::void_t<
|
||||
template<class T>
|
||||
struct is_body_writer : std::integral_constant<bool, ...> {};
|
||||
#else
|
||||
template<class T, class = beast::detail::void_t<>>
|
||||
struct is_body_writer : std::true_type {};
|
||||
template<class T, class = void>
|
||||
struct is_body_writer : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct is_body_writer<T, beast::detail::void_t<decltype(
|
||||
std::declval<typename T::writer::mutable_buffers_type>(),
|
||||
std::declval<T::writer>().init(
|
||||
std::declval<boost::optional<std::uint64_t>>()),
|
||||
std::declval<T::writer>().prepare(
|
||||
std::declval<std::size_t>()),
|
||||
std::declval<T::writer>().commit(
|
||||
std::declval<std::size_t>()),
|
||||
std::declval<T::writer>().finish()
|
||||
)> > : std::integral_constant<bool,
|
||||
is_mutable_buffer_sequence<
|
||||
typename T::writer::mutable_buffers_type>::value &&
|
||||
std::is_convertible<decltype(
|
||||
std::declval<T::writer>().prepare(
|
||||
std::declval<std::size_t>())),
|
||||
typename T::writer::mutable_buffers_type
|
||||
>::value>
|
||||
|
||||
std::declval<typename T::writer&>().init(
|
||||
std::declval<boost::optional<std::uint64_t>>(),
|
||||
std::declval<error_code&>()),
|
||||
std::declval<typename T::writer&>().put(
|
||||
std::declval<boost::asio::const_buffers_1>(),
|
||||
std::declval<error_code&>()),
|
||||
std::declval<typename T::writer&>().finish(
|
||||
std::declval<error_code&>()),
|
||||
(void)0)>> : std::integral_constant<bool,
|
||||
true
|
||||
&& std::is_constructible<typename T::writer,
|
||||
message<true, T, detail::fields_model>&>::value
|
||||
>
|
||||
{
|
||||
};
|
||||
#endif
|
||||
|
@ -28,27 +28,35 @@
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Write some serialized message data to a stream.
|
||||
/** Write part of a message to a stream using a serializer.
|
||||
|
||||
This function is used to write serialized message data to the
|
||||
stream. The function call will block until one of the following
|
||||
conditions is true:
|
||||
This function is used to write part of a message to a stream using
|
||||
a caller-provided HTTP/1 serializer. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li One or more bytes have been transferred.
|
||||
|
||||
@li The function @ref serializer::is_done returns `true`
|
||||
|
||||
@li An error occurs on the stream.
|
||||
|
||||
In order to completely serialize a message, this function
|
||||
should be called until `sr.is_done()` returns `true`.
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
@param stream The stream to write to. This type must
|
||||
satisfy the requirements of @b SyncWriteStream.
|
||||
The amount of data actually transferred is controlled by the behavior
|
||||
of the underlying stream, performing bounded work for each call. This
|
||||
helps applications set reasonable timeouts. It also allows application-level
|
||||
flow control to function correctly. For example when using a TCP/IP based
|
||||
stream.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@see @ref async_write_some, @ref serializer
|
||||
@see serializer
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
@ -57,22 +65,29 @@ void
|
||||
write_some(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr);
|
||||
|
||||
/** Write part of a message to a stream using a serializer.
|
||||
|
||||
/** Write some serialized message data to a stream.
|
||||
|
||||
This function is used to write serialized message data to the
|
||||
stream. The function call will block until one of the following
|
||||
conditions is true:
|
||||
This function is used to write part of a message to a stream using
|
||||
a caller-provided HTTP/1 serializer. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li One or more bytes have been transferred.
|
||||
|
||||
@li The function @ref serializer::is_done returns `true`
|
||||
|
||||
@li An error occurs on the stream.
|
||||
|
||||
In order to completely serialize a message, this function
|
||||
should be called until `sr.is_done()` returns `true`.
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
@param stream The stream to write to. This type must
|
||||
satisfy the requirements of @b SyncWriteStream.
|
||||
The amount of data actually transferred is controlled by the behavior
|
||||
of the underlying stream, performing bounded work for each call. This
|
||||
helps applications set reasonable timeouts. It also allows application-level
|
||||
flow control to function correctly. For example when using a TCP/IP based
|
||||
stream.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@ -88,37 +103,47 @@ write_some(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
error_code& ec);
|
||||
|
||||
/** Start an asynchronous write of some serialized message data to a stream.
|
||||
/** Write part of a message to a stream asynchronously using a serializer.
|
||||
|
||||
This function is used to asynchronously write serialized
|
||||
message data to the stream. The function call always returns
|
||||
immediately. The asynchronous operation will continue until
|
||||
one of the following conditions is true:
|
||||
This function is used to write part of a message to a stream
|
||||
asynchronously using a caller-provided HTTP/1 serializer. The function
|
||||
call always returns immediately. The asynchronous operation will continue
|
||||
until one of the following conditions is true:
|
||||
|
||||
@li One or more bytes have been transferred.
|
||||
|
||||
@li The function @ref serializer::is_done returns `true`
|
||||
|
||||
@li An error occurs on the stream.
|
||||
|
||||
In order to completely serialize a message, this function
|
||||
should be called until `sr.is_done()` returns `true`.
|
||||
This operation is implemented in terms of zero or more calls to the stream's
|
||||
`async_write_some` function, and is known as a <em>composed operation</em>.
|
||||
The program must ensure that the stream performs no other write operations
|
||||
until this operation completes.
|
||||
|
||||
@param stream The stream to write to. This type must
|
||||
satisfy the requirements of @b SyncWriteStream.
|
||||
The amount of data actually transferred is controlled by the behavior
|
||||
of the underlying stream, performing bounded work for each call. This
|
||||
helps applications set reasonable timeouts. It also allows application-level
|
||||
flow control to function correctly. For example when using a TCP/IP based
|
||||
stream.
|
||||
|
||||
@param sr The serializer to use for writing.
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b AsyncWriteStream concept.
|
||||
|
||||
@param handler The handler to be called when the request
|
||||
completes. Copies will be made of the handler as required. The
|
||||
equivalent function signature of the handler must be:
|
||||
@param sr The serializer to use.
|
||||
|
||||
@param handler The handler to be called when the operation
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& ec // Result of operation
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
@see @ref write_some, @ref serializer
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
@ -132,98 +157,93 @@ async_write_some(AsyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
WriteHandler&& handler);
|
||||
|
||||
/** Write an HTTP/1 message to a stream.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
This function is used to write a message to a stream. The call
|
||||
will block until one of the following conditions is true:
|
||||
/** Write a header to a stream using a serializer.
|
||||
|
||||
@li The entire message is written.
|
||||
This function is used to write a header to a stream using a
|
||||
caller-provided HTTP/1 serializer. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li The function @ref serializer::is_header_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
The implementation will automatically perform chunk encoding if
|
||||
the contents of the message indicate that chunk encoding is required.
|
||||
If the semantics of the message indicate that the connection should
|
||||
be closed after the message is sent, the error thrown from this
|
||||
function will be `boost::asio::error::eof`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param msg The message to write.
|
||||
@param sr The serializer to use.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@note The implementation will call @ref serializer::split with
|
||||
the value `true` on the serializer passed in.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg);
|
||||
write_header(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr);
|
||||
|
||||
/** Write an HTTP/1 message to a stream.
|
||||
/** Write a header to a stream using a serializer.
|
||||
|
||||
This function is used to write a message to a stream. The call
|
||||
will block until one of the following conditions is true:
|
||||
This function is used to write a header to a stream using a
|
||||
caller-provided HTTP/1 serializer. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
@li The function @ref serializer::is_header_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
The implementation will automatically perform chunk encoding if
|
||||
the contents of the message indicate that chunk encoding is required.
|
||||
If the semantics of the message indicate that the connection should
|
||||
be closed after the message is sent, the error returned from this
|
||||
function will be `boost::asio::error::eof`.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param msg The message to write.
|
||||
@param sr The serializer to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
@param ec Set to indicate what error occurred, if any.
|
||||
|
||||
@note The implementation will call @ref serializer::split with
|
||||
the value `true` on the serializer passed in.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
write_header(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
error_code& ec);
|
||||
|
||||
/** Write an HTTP/1 message asynchronously to a stream.
|
||||
/** Write a header to a stream asynchronously using a serializer.
|
||||
|
||||
This function is used to asynchronously write a message to
|
||||
a stream. The function call always returns immediately. The
|
||||
asynchronous operation will continue until one of the following
|
||||
conditions is true:
|
||||
This function is used to write a header to a stream asynchronously
|
||||
using a caller-provided HTTP/1 serializer. The function call always
|
||||
returns immediately. The asynchronous operation will continue until
|
||||
one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
@li The function @ref serializer::is_header_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to
|
||||
the stream's `async_write_some` functions, and is known as a
|
||||
<em>composed operation</em>. The program must ensure that the
|
||||
stream performs no other write operations until this operation
|
||||
completes.
|
||||
|
||||
The implementation will automatically perform chunk encoding if
|
||||
the contents of the message indicate that chunk encoding is required.
|
||||
If the semantics of the message indicate that the connection should
|
||||
be closed after the message is sent, the operation will complete with
|
||||
the error set to `boost::asio::error::eof`.
|
||||
This operation is implemented in terms of zero or more calls to the stream's
|
||||
`async_write_some` function, and is known as a <em>composed operation</em>.
|
||||
The program must ensure that the stream performs no other write operations
|
||||
until this operation completes.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b AsyncWriteStream concept.
|
||||
|
||||
@param msg The message to write. The object must remain valid
|
||||
at least until the completion handler is called; ownership is
|
||||
not transferred.
|
||||
@param sr The serializer to use.
|
||||
|
||||
@param handler The handler to be called when the operation
|
||||
completes. Copies will be made of the handler as required.
|
||||
@ -235,6 +255,231 @@ write(SyncWriteStream& stream,
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
@note The implementation will call @ref serializer::split with
|
||||
the value `true` on the serializer passed in.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator, class WriteHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
#endif
|
||||
async_write_header(AsyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
WriteHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Write a complete message to a stream using a serializer.
|
||||
|
||||
This function is used to write a complete message to a stream using
|
||||
a caller-provided HTTP/1 serializer. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li The function @ref serializer::is_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr);
|
||||
|
||||
/** Write a complete message to a stream using a serializer.
|
||||
|
||||
This function is used to write a complete message to a stream using
|
||||
a caller-provided HTTP/1 serializer. The call will block until one
|
||||
of the following conditions is true:
|
||||
|
||||
@li The function @ref serializer::is_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls
|
||||
to the stream's `write_some` function.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator>
|
||||
void
|
||||
write(SyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
error_code& ec);
|
||||
|
||||
/** Write a complete message to a stream asynchronously using a serializer.
|
||||
|
||||
This function is used to write a complete message to a stream
|
||||
asynchronously using a caller-provided HTTP/1 serializer. The
|
||||
function call always returns immediately. The asynchronous
|
||||
operation will continue until one of the following conditions is true:
|
||||
|
||||
@li The function @ref serializer::is_done returns `true`
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the stream's
|
||||
`async_write_some` function, and is known as a <em>composed operation</em>.
|
||||
The program must ensure that the stream performs no other write operations
|
||||
until this operation completes.
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b AsyncWriteStream concept.
|
||||
|
||||
@param sr The serializer to use.
|
||||
|
||||
@param handler The handler to be called when the operation
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
@see @ref serializer
|
||||
*/
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class Decorator, class Allocator, class WriteHandler>
|
||||
#if BEAST_DOXYGEN
|
||||
void_or_deduced
|
||||
#else
|
||||
async_return_type<WriteHandler, void(error_code)>
|
||||
#endif
|
||||
async_write(AsyncWriteStream& stream, serializer<
|
||||
isRequest, Body, Fields, Decorator, Allocator>& sr,
|
||||
WriteHandler&& handler);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** Write a complete message to a stream.
|
||||
|
||||
This function is used to write a complete message to a stream using
|
||||
HTTP/1. The call will block until one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`write_some` function. The algorithm will use a temporary @ref serializer
|
||||
with an empty chunk decorator to produce buffers. If the semantics of the
|
||||
message indicate that the connection should be closed after the message is
|
||||
sent, the error delivered by this function will be @ref error::end_of_stream
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param msg The message to write.
|
||||
|
||||
@throws system_error Thrown on failure.
|
||||
|
||||
@see @ref message
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg);
|
||||
|
||||
/** Write a complete message to a stream.
|
||||
|
||||
This function is used to write a complete message to a stream using
|
||||
HTTP/1. The call will block until one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of one or more calls to the stream's
|
||||
`write_some` function. The algorithm will use a temporary @ref serializer
|
||||
with an empty chunk decorator to produce buffers. If the semantics of the
|
||||
message indicate that the connection should be closed after the message is
|
||||
sent, the error delivered by this function will be @ref error::end_of_stream
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b SyncWriteStream concept.
|
||||
|
||||
@param msg The message to write.
|
||||
|
||||
@param ec Set to the error, if any occurred.
|
||||
|
||||
@see @ref message
|
||||
*/
|
||||
template<class SyncWriteStream,
|
||||
bool isRequest, class Body, class Fields>
|
||||
void
|
||||
write(SyncWriteStream& stream,
|
||||
message<isRequest, Body, Fields> const& msg,
|
||||
error_code& ec);
|
||||
|
||||
/** Write a complete message to a stream asynchronously.
|
||||
|
||||
This function is used to write a complete message to a stream asynchronously
|
||||
using HTTP/1. The function call always returns immediately. The asynchronous
|
||||
operation will continue until one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the stream's
|
||||
`async_write_some` function, and is known as a <em>composed operation</em>.
|
||||
The program must ensure that the stream performs no other write operations
|
||||
until this operation completes. The algorithm will use a temporary
|
||||
@ref serializer with an empty chunk decorator to produce buffers. If
|
||||
the semantics of the message indicate that the connection should be
|
||||
closed after the message is sent, the error delivered by this function
|
||||
will be @ref error::end_of_stream
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b AsyncWriteStream concept.
|
||||
|
||||
@param msg The message to write. The object must remain valid at least
|
||||
until the completion handler is called; ownership is not transferred.
|
||||
|
||||
@param handler The handler to be called when the operation
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
|
||||
@see @ref message
|
||||
*/
|
||||
template<class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
|
@ -20,7 +20,7 @@ namespace websocket {
|
||||
namespace detail {
|
||||
|
||||
// A container that holds a suspended, asynchronous composed
|
||||
// operation. The contained object may be invoked later to
|
||||
// operation. The contained object may be invoked later to
|
||||
// resume the operation, or the container may be destroyed.
|
||||
//
|
||||
class pausation
|
||||
|
@ -281,7 +281,7 @@ operator()(error_code ec,
|
||||
|
||||
case 1:
|
||||
{
|
||||
BOOST_ASSERT(d.p.got_header());
|
||||
BOOST_ASSERT(d.p.is_header_done());
|
||||
d.ws.stream_.buffer().consume(bytes_used);
|
||||
// Arguments from our state must be
|
||||
// moved to the stack before releasing
|
||||
|
@ -96,7 +96,7 @@ do_accept(
|
||||
next_layer(), stream_.buffer(), p, ec);
|
||||
if(ec)
|
||||
return;
|
||||
BOOST_ASSERT(p.got_header());
|
||||
BOOST_ASSERT(p.is_header_done());
|
||||
stream_.buffer().consume(bytes_used);
|
||||
do_accept(p.get(), decorator, ec);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
// Test that header file is self-contained.
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
#include <functional>
|
||||
|
||||
@ -16,6 +17,21 @@ namespace beast {
|
||||
class bind_handler_test : public unit_test::suite
|
||||
{
|
||||
public:
|
||||
struct handler
|
||||
{
|
||||
void
|
||||
operator()() const;
|
||||
};
|
||||
|
||||
#if 0
|
||||
// This function should fail to compile
|
||||
void
|
||||
failStdBind()
|
||||
{
|
||||
std::bind(bind_handler(handler{}));
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
callback(int v)
|
||||
{
|
||||
|
@ -40,6 +40,7 @@ add_executable (http-bench
|
||||
${BEAST_INCLUDES}
|
||||
${EXTRAS_INCLUDES}
|
||||
nodejs_parser.hpp
|
||||
message_fuzz.hpp
|
||||
../../extras/beast/unit_test/main.cpp
|
||||
nodejs_parser.cpp
|
||||
parser_bench.cpp
|
||||
|
@ -148,19 +148,18 @@ public:
|
||||
return {s, N-1};
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
template<bool isRequest, class Derived>
|
||||
static
|
||||
std::size_t
|
||||
feed(boost::asio::const_buffer buffer,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::const_buffers_1;
|
||||
std::size_t used = 0;
|
||||
for(;;)
|
||||
{
|
||||
auto const n = parser.write(
|
||||
auto const n = parser.put(
|
||||
const_buffers_1{buffer}, ec);
|
||||
if(ec)
|
||||
return 0;
|
||||
@ -168,7 +167,7 @@ public:
|
||||
break;
|
||||
buffer = buffer + n;
|
||||
used += n;
|
||||
if(parser.is_complete())
|
||||
if(parser.is_done())
|
||||
break;
|
||||
if(buffer_size(buffer) == 0)
|
||||
break;
|
||||
@ -177,11 +176,11 @@ public:
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
static
|
||||
std::size_t
|
||||
feed(ConstBufferSequence const& buffers,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
@ -191,14 +190,14 @@ public:
|
||||
for(;;)
|
||||
{
|
||||
auto const n =
|
||||
parser.write(cb, ec);
|
||||
parser.put(cb, ec);
|
||||
if(ec)
|
||||
return 0;
|
||||
if(n == 0)
|
||||
break;
|
||||
cb.consume(n);
|
||||
used += n;
|
||||
if(parser.is_complete())
|
||||
if(parser.is_done())
|
||||
break;
|
||||
if(buffer_size(cb) == 0)
|
||||
break;
|
||||
@ -206,12 +205,11 @@ public:
|
||||
return used;
|
||||
}
|
||||
|
||||
template<
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
template<bool isRequest, class Derived>
|
||||
static
|
||||
std::size_t
|
||||
feed(boost::asio::const_buffers_1 buffers,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
return feed(*buffers.begin(), parser, ec);
|
||||
@ -225,7 +223,7 @@ public:
|
||||
using boost::asio::buffer;
|
||||
test_parser<isRequest> p;
|
||||
if(skipBody)
|
||||
p.skip_body();
|
||||
p.skip(true);
|
||||
error_code ec;
|
||||
auto const n = feed(buffer(
|
||||
s.data(), s.size()), p, ec);
|
||||
@ -233,8 +231,8 @@ public:
|
||||
return;
|
||||
if(! BEAST_EXPECT(n == s.size()))
|
||||
return;
|
||||
if(p.state() == parse_state::body_to_eof)
|
||||
p.write_eof(ec);
|
||||
if(p.need_eof())
|
||||
p.put_eof(ec);
|
||||
if(BEAST_EXPECTS(! ec, ec.message()))
|
||||
pred(p);
|
||||
}
|
||||
@ -257,18 +255,19 @@ public:
|
||||
using boost::asio::buffer;
|
||||
test_parser<isRequest> p;
|
||||
if(skipBody)
|
||||
p.skip_body();
|
||||
p.skip(true);
|
||||
error_code ec;
|
||||
feed(buffer(
|
||||
s.data(), s.size()), p, ec);
|
||||
if(! ec && ev)
|
||||
p.write_eof(ec);
|
||||
p.put_eof(ec);
|
||||
BEAST_EXPECTS(ec == ev, ec.message());
|
||||
}
|
||||
|
||||
void
|
||||
testFlatten()
|
||||
{
|
||||
#if 0
|
||||
using boost::asio::buffer;
|
||||
{
|
||||
std::string const s =
|
||||
@ -289,7 +288,7 @@ public:
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
feed(buffer_cat(b1, b2), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_done());
|
||||
}
|
||||
}
|
||||
{
|
||||
@ -310,9 +309,10 @@ public:
|
||||
ec = {};
|
||||
feed(buffer_cat(b1, b2), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
p.write_eof(ec);
|
||||
p.put_eof(ec);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Check that all callbacks are invoked
|
||||
@ -331,7 +331,7 @@ public:
|
||||
"*";
|
||||
feed(buffer(s), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_done());
|
||||
BEAST_EXPECT(p.got_on_begin);
|
||||
BEAST_EXPECT(p.got_on_field);
|
||||
BEAST_EXPECT(p.got_on_header);
|
||||
@ -350,7 +350,7 @@ public:
|
||||
"*";
|
||||
feed(buffer(s), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_done());
|
||||
BEAST_EXPECT(p.got_on_begin);
|
||||
BEAST_EXPECT(p.got_on_field);
|
||||
BEAST_EXPECT(p.got_on_header);
|
||||
@ -747,6 +747,7 @@ public:
|
||||
|
||||
void testBody()
|
||||
{
|
||||
#if 0
|
||||
using boost::asio::buffer;
|
||||
good<true>(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
@ -774,7 +775,7 @@ public:
|
||||
buf("67890")),
|
||||
p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_done());
|
||||
}
|
||||
|
||||
// request without Content-Length or
|
||||
@ -787,7 +788,7 @@ public:
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_done());
|
||||
}
|
||||
{
|
||||
error_code ec;
|
||||
@ -797,7 +798,7 @@ public:
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_done());
|
||||
}
|
||||
|
||||
// response without Content-Length or
|
||||
@ -810,17 +811,17 @@ public:
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(! p.is_complete());
|
||||
BEAST_EXPECT(! p.is_done());
|
||||
BEAST_EXPECT(p.state() == parse_state::body_to_eof);
|
||||
feed(buf(
|
||||
"hello"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(! p.is_complete());
|
||||
BEAST_EXPECT(! p.is_done());
|
||||
BEAST_EXPECT(p.state() == parse_state::body_to_eof);
|
||||
p.write_eof(ec);
|
||||
p.put_eof(ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_done());
|
||||
}
|
||||
|
||||
// 304 "Not Modified" response does not require eof
|
||||
@ -832,7 +833,7 @@ public:
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_done());
|
||||
}
|
||||
|
||||
// Chunked response does not require eof
|
||||
@ -845,12 +846,12 @@ public:
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(! p.is_complete());
|
||||
BEAST_EXPECT(! p.is_done());
|
||||
feed(buf(
|
||||
"0\r\n\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_done());
|
||||
}
|
||||
|
||||
// restart: 1.0 assumes Connection: close
|
||||
@ -862,7 +863,7 @@ public:
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_done());
|
||||
}
|
||||
|
||||
// restart: 1.1 assumes Connection: keep-alive
|
||||
@ -874,7 +875,7 @@ public:
|
||||
"\r\n"
|
||||
), p, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_done());
|
||||
}
|
||||
|
||||
bad<true>(
|
||||
@ -882,8 +883,10 @@ public:
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n",
|
||||
error::partial_message);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
template<bool isRequest>
|
||||
void
|
||||
check_header(
|
||||
@ -898,6 +901,7 @@ public:
|
||||
BEAST_EXPECT(! p.got_on_complete);
|
||||
BEAST_EXPECT(p.state() != parse_state::header);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
testSplit()
|
||||
@ -921,7 +925,7 @@ public:
|
||||
BEAST_EXPECT(! p.got_on_chunk);
|
||||
BEAST_EXPECT(! p.got_on_complete);
|
||||
BEAST_EXPECT(p.state() != parse_state::header);
|
||||
BEAST_EXPECT(! p.is_complete());
|
||||
BEAST_EXPECT(! p.is_done());
|
||||
BEAST_EXPECT(p.body.empty());
|
||||
b.consume(n);
|
||||
p.resume();
|
||||
@ -933,7 +937,7 @@ public:
|
||||
BEAST_EXPECT(p.got_on_body);
|
||||
BEAST_EXPECT(! p.got_on_chunk);
|
||||
BEAST_EXPECT(p.got_on_complete);
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_done());
|
||||
BEAST_EXPECT(p.body == "*****");
|
||||
#endif
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,13 @@ class header_parser_test
|
||||
, public test::enable_yield_to
|
||||
{
|
||||
public:
|
||||
static
|
||||
boost::asio::const_buffers_1
|
||||
buf(string_view s)
|
||||
{
|
||||
return {s.data(), s.size()};
|
||||
}
|
||||
|
||||
void
|
||||
testParse()
|
||||
{
|
||||
@ -34,7 +41,7 @@ public:
|
||||
flat_buffer db{1024};
|
||||
header_parser<true, fields> p;
|
||||
read_some(is, db, p);
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_header_done());
|
||||
}
|
||||
{
|
||||
test::string_istream is{ios_,
|
||||
@ -47,8 +54,8 @@ public:
|
||||
flat_buffer db{1024};
|
||||
header_parser<true, fields> p;
|
||||
read_some(is, db, p);
|
||||
BEAST_EXPECT(! p.is_complete());
|
||||
BEAST_EXPECT(p.state() == parse_state::body);
|
||||
BEAST_EXPECT(p.is_header_done());
|
||||
BEAST_EXPECT(! p.is_done());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,10 +14,10 @@
|
||||
#include <beast/test/string_istream.hpp>
|
||||
#include <beast/test/string_ostream.hpp>
|
||||
#include <beast/test/yield_to.hpp>
|
||||
#include <beast/core/consuming_buffers.hpp>
|
||||
#include <beast/core/flat_buffer.hpp>
|
||||
#include <beast/core/multi_buffer.hpp>
|
||||
#include <beast/http/header_parser.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/core/ostream.hpp>
|
||||
#include <beast/http/read.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <boost/system/system_error.hpp>
|
||||
@ -30,41 +30,117 @@ class message_parser_test
|
||||
, public beast::test::enable_yield_to
|
||||
{
|
||||
public:
|
||||
template<bool isRequest, class Pred>
|
||||
void
|
||||
testMatrix(std::string const& s, Pred const& pred)
|
||||
template<bool isRequest>
|
||||
using parser_type =
|
||||
message_parser<isRequest, string_body, fields>;
|
||||
|
||||
static
|
||||
boost::asio::const_buffers_1
|
||||
buf(string_view s)
|
||||
{
|
||||
beast::test::string_istream ss{get_io_service(), s};
|
||||
error_code ec;
|
||||
#if 0
|
||||
multi_buffer buffer;
|
||||
#else
|
||||
flat_buffer buffer{1024};
|
||||
#endif
|
||||
message<isRequest, string_body, fields> m;
|
||||
read(ss, buffer, m, ec);
|
||||
if(! BEAST_EXPECTS(! ec, ec.message()))
|
||||
return;
|
||||
pred(m);
|
||||
return {s.data(), s.size()};
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence,
|
||||
bool isRequest, class Derived>
|
||||
static
|
||||
void
|
||||
put(ConstBufferSequence const& buffers,
|
||||
basic_parser<isRequest, Derived>& p,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
consuming_buffers<ConstBufferSequence> cb{buffers};
|
||||
for(;;)
|
||||
{
|
||||
auto const used = p.put(cb, ec);
|
||||
cb.consume(used);
|
||||
if(ec)
|
||||
return;
|
||||
if(p.need_eof() &&
|
||||
buffer_size(cb) == 0)
|
||||
{
|
||||
p.put_eof(ec);
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
if(p.is_done())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isRequest, class F>
|
||||
void
|
||||
doMatrix(string_view s0, F const& f)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
// parse a single buffer
|
||||
{
|
||||
auto s = s0;
|
||||
error_code ec;
|
||||
parser_type<isRequest> p;
|
||||
put(buffer(s.data(), s.size()), p, ec);
|
||||
if(! BEAST_EXPECTS(! ec, ec.message()))
|
||||
return;
|
||||
f(p);
|
||||
}
|
||||
// parse two buffers
|
||||
for(auto n = s0.size() - 1; n >= 1; --n)
|
||||
{
|
||||
auto s = s0;
|
||||
error_code ec;
|
||||
parser_type<isRequest> p;
|
||||
p.eager(true);
|
||||
auto used =
|
||||
p.put(buffer(s.data(), n), ec);
|
||||
s.remove_prefix(used);
|
||||
if(ec == error::need_more)
|
||||
ec = {};
|
||||
if(! BEAST_EXPECTS(! ec, ec.message()))
|
||||
continue;
|
||||
BEAST_EXPECT(! p.is_done());
|
||||
used = p.put(
|
||||
buffer(s.data(), s.size()), ec);
|
||||
s.remove_prefix(used);
|
||||
if(! BEAST_EXPECTS(! ec, ec.message()))
|
||||
continue;
|
||||
BEAST_EXPECT(s.empty());
|
||||
if(p.need_eof())
|
||||
{
|
||||
p.put_eof(ec);
|
||||
if(! BEAST_EXPECTS(! ec, ec.message()))
|
||||
continue;
|
||||
}
|
||||
if(BEAST_EXPECT(p.is_done()))
|
||||
f(p);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testRead()
|
||||
testParse()
|
||||
{
|
||||
testMatrix<false>(
|
||||
doMatrix<false>(
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"Server: test\r\n"
|
||||
"\r\n"
|
||||
"*******",
|
||||
[&](message<false, string_body, fields> const& m)
|
||||
"Hello, world!",
|
||||
[&](parser_type<false> const& p)
|
||||
{
|
||||
BEAST_EXPECTS(m.body == "*******",
|
||||
"body='" + m.body + "'");
|
||||
auto const& m = p.get();
|
||||
BEAST_EXPECT(! p.is_chunked());
|
||||
BEAST_EXPECT(p.need_eof());
|
||||
BEAST_EXPECT(p.content_length() == boost::none);
|
||||
BEAST_EXPECT(m.version == 10);
|
||||
BEAST_EXPECT(m.status == 200);
|
||||
BEAST_EXPECT(m.reason() == "OK");
|
||||
BEAST_EXPECT(m.fields["Server"] == "test");
|
||||
BEAST_EXPECT(m.body == "Hello, world!");
|
||||
}
|
||||
);
|
||||
testMatrix<false>(
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
doMatrix<false>(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: test\r\n"
|
||||
"Expect: Expires, MD5-Fingerprint\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
"5\r\n"
|
||||
@ -75,132 +151,137 @@ public:
|
||||
"Expires: never\r\n"
|
||||
"MD5-Fingerprint: -\r\n"
|
||||
"\r\n",
|
||||
[&](message<false, string_body, fields> const& m)
|
||||
[&](parser_type<false> const& p)
|
||||
{
|
||||
auto const& m = p.get();
|
||||
BEAST_EXPECT(! p.need_eof());
|
||||
BEAST_EXPECT(p.is_chunked());
|
||||
BEAST_EXPECT(p.content_length() == boost::none);
|
||||
BEAST_EXPECT(m.version == 11);
|
||||
BEAST_EXPECT(m.status == 200);
|
||||
BEAST_EXPECT(m.reason() == "OK");
|
||||
BEAST_EXPECT(m.fields["Server"] == "test");
|
||||
BEAST_EXPECT(m.fields["Transfer-Encoding"] == "chunked");
|
||||
BEAST_EXPECT(m.fields["Expires"] == "never");
|
||||
BEAST_EXPECT(m.fields["MD5-Fingerprint"] == "-");
|
||||
BEAST_EXPECT(m.body == "*****--");
|
||||
}
|
||||
);
|
||||
testMatrix<false>(
|
||||
doMatrix<false>(
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"Server: test\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"\r\n"
|
||||
"*****",
|
||||
[&](message<false, string_body, fields> const& m)
|
||||
[&](parser_type<false> const& p)
|
||||
{
|
||||
auto const& m = p.get();
|
||||
BEAST_EXPECT(m.body == "*****");
|
||||
}
|
||||
);
|
||||
testMatrix<true>(
|
||||
doMatrix<true>(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"\r\n",
|
||||
[&](message<true, string_body, fields> const& m)
|
||||
[&](parser_type<true> const& p)
|
||||
{
|
||||
auto const& m = p.get();
|
||||
BEAST_EXPECT(m.method() == "GET");
|
||||
BEAST_EXPECT(m.target() == "/");
|
||||
BEAST_EXPECT(m.version == 11);
|
||||
BEAST_EXPECT(! p.need_eof());
|
||||
BEAST_EXPECT(! p.is_chunked());
|
||||
BEAST_EXPECT(p.content_length() == boost::none);
|
||||
}
|
||||
);
|
||||
testMatrix<true>(
|
||||
doMatrix<true>(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"X: \t x \t \r\n"
|
||||
"\r\n",
|
||||
[&](message<true, string_body, fields> const& m)
|
||||
[&](parser_type<true> const& p)
|
||||
{
|
||||
auto const& m = p.get();
|
||||
BEAST_EXPECT(m.fields["X"] == "x");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
testParse()
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
// test eager(true)
|
||||
{
|
||||
error_code ec;
|
||||
beast::test::string_istream is{
|
||||
get_io_service(),
|
||||
parser_type<true> p;
|
||||
p.eager(true);
|
||||
p.put(buf(
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n"
|
||||
"*"};
|
||||
flat_buffer b{1024};
|
||||
message_parser<true, string_body, fields> p;
|
||||
read(is, b, p, ec);
|
||||
"*")
|
||||
, ec);
|
||||
auto const& m = p.get();
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(! ec);
|
||||
BEAST_EXPECT(p.is_done());
|
||||
BEAST_EXPECT(p.is_header_done());
|
||||
BEAST_EXPECT(! p.need_eof());
|
||||
BEAST_EXPECT(m.method() == "GET");
|
||||
BEAST_EXPECT(m.target() == "/");
|
||||
BEAST_EXPECT(m.version == 11);
|
||||
BEAST_EXPECT(m.fields["User-Agent"] == "test");
|
||||
BEAST_EXPECT(m.body == "*");
|
||||
}
|
||||
#if 0
|
||||
{
|
||||
// test partial parsing of final chunk
|
||||
// parse through the chunk body
|
||||
beast::test::string_istream is{
|
||||
get_io_service(), ""};
|
||||
multi_buffer b;
|
||||
b <<
|
||||
error_code ec;
|
||||
flat_buffer b;
|
||||
parser_type<true> p;
|
||||
p.eager(true);
|
||||
ostream(b) <<
|
||||
"PUT / HTTP/1.1\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
"1\r\n"
|
||||
"*";
|
||||
error_code ec;
|
||||
message_parser<true, string_body, fields> p;
|
||||
read(is, b, p, ec);
|
||||
BEAST_EXPECT(b.size() == 0);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(!p.is_complete());
|
||||
auto used = p.put(b.data(), ec);
|
||||
b.consume(used);
|
||||
BEAST_EXPECT(! ec);
|
||||
BEAST_EXPECT(! p.is_done());
|
||||
BEAST_EXPECT(p.get().body == "*");
|
||||
b << "\r\n0;d;e=3;f=\"4\"\r\n"
|
||||
ostream(b) <<
|
||||
"\r\n"
|
||||
"0;d;e=3;f=\"4\"\r\n"
|
||||
"Expires: never\r\n"
|
||||
"MD5-Fingerprint: -\r\n";
|
||||
// incomplete parse, missing the final crlf
|
||||
BEAST_EXPECT(p.write(b.data(), ec) == 0);
|
||||
used = p.put(b.data(), ec);
|
||||
b.consume(used);
|
||||
BEAST_EXPECT(ec == error::need_more);
|
||||
ec = {};
|
||||
BEAST_EXPECT(! p.is_done());
|
||||
ostream(b) <<
|
||||
"\r\n"; // final crlf to end message
|
||||
used = p.put(b.data(), ec);
|
||||
b.consume(used);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(!p.is_complete());
|
||||
b << "\r\n"; // final crlf to end message
|
||||
BEAST_EXPECT(p.write(b.data(), ec) == b.size());
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
}
|
||||
{
|
||||
error_code ec;
|
||||
message_parser<false, string_body, fields> p;
|
||||
std::string const s =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Server: test\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n"
|
||||
"*";
|
||||
p.write(buffer(s), ec);
|
||||
auto const& m = p.get();
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(m.status == 200);
|
||||
BEAST_EXPECT(m.reason() == "OK");
|
||||
BEAST_EXPECT(m.version == 11);
|
||||
BEAST_EXPECT(m.fields["Server"] == "test");
|
||||
BEAST_EXPECT(m.body == "*");
|
||||
BEAST_EXPECT(p.is_done());
|
||||
}
|
||||
// skip body
|
||||
{
|
||||
error_code ec;
|
||||
message_parser<false, string_body, fields> p;
|
||||
std::string const s =
|
||||
"HTTP/1.1 200 Connection Established\r\n"
|
||||
"Proxy-Agent: Zscaler/5.1\r\n"
|
||||
"\r\n";
|
||||
p.skip_body();
|
||||
p.write(buffer(s), ec);
|
||||
p.skip(true);
|
||||
p.put(buf(
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Length: 5\r\n"
|
||||
"\r\n"
|
||||
"*****")
|
||||
, ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p.is_complete());
|
||||
BEAST_EXPECT(p.is_done());
|
||||
BEAST_EXPECT(p.is_header_done());
|
||||
BEAST_EXPECT(p.content_length() &&
|
||||
*p.content_length() == 5);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -219,8 +300,8 @@ public:
|
||||
read_some(ss, b, p0, ec);
|
||||
b.consume(bytes_used);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
BEAST_EXPECT(p0.state() != parse_state::header);
|
||||
BEAST_EXPECT(! p0.is_complete());
|
||||
BEAST_EXPECT(p0.is_header_done());
|
||||
BEAST_EXPECT(! p0.is_done());
|
||||
message_parser<true,
|
||||
string_body, fields> p1{std::move(p0)};
|
||||
read(ss, b, p1, ec);
|
||||
@ -228,12 +309,57 @@ public:
|
||||
BEAST_EXPECT(p1.get().body == "*****");
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
testNeedMore()
|
||||
{
|
||||
error_code ec;
|
||||
std::size_t used;
|
||||
{
|
||||
DynamicBuffer b;
|
||||
parser_type<true> p;
|
||||
ostream(b) <<
|
||||
"GET / HTTP/1.1\r\n";
|
||||
used = p.put(b.data(), ec);
|
||||
BEAST_EXPECTS(ec == error::need_more, ec.message());
|
||||
b.consume(used);
|
||||
ec = {};
|
||||
ostream(b) <<
|
||||
"User-Agent: test\r\n"
|
||||
"\r\n";
|
||||
used = p.put(b.data(), ec);
|
||||
BEAST_EXPECTS(! ec, ec.message());
|
||||
b.consume(used);
|
||||
BEAST_EXPECT(p.is_done());
|
||||
BEAST_EXPECT(p.is_header_done());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testGotSome()
|
||||
{
|
||||
error_code ec;
|
||||
parser_type<true> p;
|
||||
auto used = p.put(buf(""), ec);
|
||||
BEAST_EXPECT(ec == error::need_more);
|
||||
BEAST_EXPECT(! p.got_some());
|
||||
BEAST_EXPECT(used == 0);
|
||||
ec = {};
|
||||
used = p.put(buf("G"), ec);
|
||||
BEAST_EXPECT(ec == error::need_more);
|
||||
BEAST_EXPECT(p.got_some());
|
||||
BEAST_EXPECT(used == 0);
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testRead();
|
||||
testParse();
|
||||
testExpect100Continue();
|
||||
testNeedMore<flat_buffer>();
|
||||
testNeedMore<multi_buffer>();
|
||||
testGotSome();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <beast/http.hpp>
|
||||
#include <beast/core/consuming_buffers.hpp>
|
||||
#include <beast/core/ostream.hpp>
|
||||
#include <beast/core/flat_buffer.hpp>
|
||||
#include <beast/core/multi_buffer.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
@ -26,7 +27,8 @@ class parser_bench_test : public beast::unit_test::suite
|
||||
public:
|
||||
static std::size_t constexpr N = 2000;
|
||||
|
||||
using corpus = std::vector<multi_buffer>;
|
||||
//using corpus = std::vector<multi_buffer>;
|
||||
using corpus = std::vector<flat_buffer>;
|
||||
|
||||
corpus creq_;
|
||||
corpus cres_;
|
||||
@ -41,22 +43,17 @@ public:
|
||||
std::string>(buffers(bs));
|
||||
}
|
||||
|
||||
parser_bench_test()
|
||||
{
|
||||
creq_ = build_corpus(N/2, std::true_type{});
|
||||
cres_ = build_corpus(N/2, std::false_type{});
|
||||
}
|
||||
|
||||
corpus
|
||||
build_corpus(std::size_t n, std::true_type)
|
||||
{
|
||||
corpus v;
|
||||
v.resize(N);
|
||||
v.resize(n);
|
||||
message_fuzz mg;
|
||||
for(std::size_t i = 0; i < n; ++i)
|
||||
{
|
||||
mg.request(v[i]);
|
||||
size_ += v[i].size();
|
||||
BEAST_EXPECT(v[i].size() > 0);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
@ -65,22 +62,23 @@ public:
|
||||
build_corpus(std::size_t n, std::false_type)
|
||||
{
|
||||
corpus v;
|
||||
v.resize(N);
|
||||
v.resize(n);
|
||||
message_fuzz mg;
|
||||
for(std::size_t i = 0; i < n; ++i)
|
||||
{
|
||||
mg.response(v[i]);
|
||||
size_ += v[i].size();
|
||||
BEAST_EXPECT(v[i].size() > 0);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence,
|
||||
bool isRequest, bool isDirect, class Derived>
|
||||
bool isRequest, class Derived>
|
||||
static
|
||||
std::size_t
|
||||
feed(ConstBufferSequence const& buffers,
|
||||
basic_parser<isRequest, isDirect, Derived>& parser,
|
||||
basic_parser<isRequest, Derived>& parser,
|
||||
error_code& ec)
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
@ -90,14 +88,14 @@ public:
|
||||
for(;;)
|
||||
{
|
||||
auto const n =
|
||||
parser.write(cb, ec);
|
||||
parser.put(cb, ec);
|
||||
if(ec)
|
||||
return 0;
|
||||
if(n == 0)
|
||||
break;
|
||||
cb.consume(n);
|
||||
used += n;
|
||||
if(parser.is_complete())
|
||||
if(parser.is_done())
|
||||
break;
|
||||
if(buffer_size(cb) == 0)
|
||||
break;
|
||||
@ -155,14 +153,13 @@ public:
|
||||
|
||||
template<bool isRequest>
|
||||
struct null_parser :
|
||||
basic_parser<isRequest, true,
|
||||
null_parser<isRequest>>
|
||||
basic_parser<isRequest, null_parser<isRequest>>
|
||||
{
|
||||
};
|
||||
|
||||
template<bool isRequest, class Body, class Fields>
|
||||
struct bench_parser : basic_parser<
|
||||
isRequest, false, bench_parser<isRequest, Body, Fields>>
|
||||
isRequest, bench_parser<isRequest, Body, Fields>>
|
||||
{
|
||||
using mutable_buffers_type =
|
||||
boost::asio::mutable_buffers_1;
|
||||
@ -194,13 +191,8 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
on_body(error_code& ec)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_body(std::uint64_t content_length,
|
||||
error_code& ec)
|
||||
on_body(boost::optional<std::uint64_t> const&,
|
||||
error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
@ -217,12 +209,6 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_body(string_view const&,
|
||||
error_code&)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
on_complete(error_code&)
|
||||
{
|
||||
@ -235,6 +221,9 @@ public:
|
||||
static std::size_t constexpr Trials = 3;
|
||||
static std::size_t constexpr Repeat = 500;
|
||||
|
||||
creq_ = build_corpus(N/2, std::true_type{});
|
||||
cres_ = build_corpus(N/2, std::false_type{});
|
||||
|
||||
log << "sizeof(request parser) == " <<
|
||||
sizeof(null_parser<true>) << '\n';
|
||||
|
||||
@ -245,16 +234,6 @@ public:
|
||||
((Repeat * size_ + 512) / 1024) << "KB in " <<
|
||||
(Repeat * (creq_.size() + cres_.size())) << " messages";
|
||||
|
||||
timedTest(Trials, "nodejs_parser",
|
||||
[&]
|
||||
{
|
||||
testParser1<nodejs_parser<
|
||||
true, dynamic_body, fields>>(
|
||||
Repeat, creq_);
|
||||
testParser1<nodejs_parser<
|
||||
false, dynamic_body, fields>>(
|
||||
Repeat, cres_);
|
||||
});
|
||||
timedTest(Trials, "http::basic_parser",
|
||||
[&]
|
||||
{
|
||||
@ -265,6 +244,16 @@ public:
|
||||
false, dynamic_body, fields>>(
|
||||
Repeat, cres_);
|
||||
});
|
||||
timedTest(Trials, "nodejs_parser",
|
||||
[&]
|
||||
{
|
||||
testParser1<nodejs_parser<
|
||||
true, dynamic_body, fields>>(
|
||||
Repeat, creq_);
|
||||
testParser1<nodejs_parser<
|
||||
false, dynamic_body, fields>>(
|
||||
Repeat, cres_);
|
||||
});
|
||||
pass();
|
||||
}
|
||||
|
||||
|
@ -10,11 +10,14 @@
|
||||
|
||||
#include "test_parser.hpp"
|
||||
|
||||
#include <beast/core/ostream.hpp>
|
||||
#include <beast/core/static_buffer.hpp>
|
||||
#include <beast/http/fields.hpp>
|
||||
#include <beast/http/dynamic_body.hpp>
|
||||
#include <beast/http/header_parser.hpp>
|
||||
#include <beast/http/string_body.hpp>
|
||||
#include <beast/test/fail_stream.hpp>
|
||||
#include <beast/test/pipe_stream.hpp>
|
||||
#include <beast/test/string_istream.hpp>
|
||||
#include <beast/test/yield_to.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
@ -108,7 +111,7 @@ public:
|
||||
{
|
||||
multi_buffer b;
|
||||
test::string_istream ss(ios_, "GET / X");
|
||||
message_parser<true, dynamic_body, fields> p;
|
||||
request_parser<dynamic_body> p;
|
||||
read(ss, b, p);
|
||||
fail();
|
||||
}
|
||||
@ -118,6 +121,52 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testBufferOverflow()
|
||||
{
|
||||
{
|
||||
test::pipe p{ios_};
|
||||
ostream(p.server.buffer) <<
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
"10\r\n"
|
||||
"****************\r\n"
|
||||
"0\r\n\r\n";
|
||||
static_buffer_n<1024> b;
|
||||
request<string_body> req;
|
||||
try
|
||||
{
|
||||
read(p.server, b, req);
|
||||
pass();
|
||||
}
|
||||
catch(std::exception const& e)
|
||||
{
|
||||
fail(e.what(), __FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
{
|
||||
test::pipe p{ios_};
|
||||
ostream(p.server.buffer) <<
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
"Transfer-Encoding: chunked\r\n"
|
||||
"\r\n"
|
||||
"10\r\n"
|
||||
"****************\r\n"
|
||||
"0\r\n\r\n";
|
||||
error_code ec;
|
||||
static_buffer_n<10> b;
|
||||
request<string_body> req;
|
||||
read(p.server, b, req, ec);
|
||||
BEAST_EXPECTS(ec == error::buffer_overflow,
|
||||
ec.message());
|
||||
}
|
||||
}
|
||||
|
||||
void testFailures(yield_context do_yield)
|
||||
{
|
||||
char const* req[] = {
|
||||
@ -318,6 +367,7 @@ public:
|
||||
run() override
|
||||
{
|
||||
testThrow();
|
||||
testBufferOverflow();
|
||||
|
||||
yield_to([&](yield_context yield){
|
||||
testFailures(yield); });
|
||||
|
@ -16,8 +16,7 @@ namespace http {
|
||||
|
||||
template<bool isRequest>
|
||||
class test_parser
|
||||
: public basic_parser<isRequest, false,
|
||||
test_parser<isRequest>>
|
||||
: public basic_parser<isRequest, test_parser<isRequest>>
|
||||
{
|
||||
test::fail_counter* fc_ = nullptr;
|
||||
|
||||
@ -36,8 +35,6 @@ public:
|
||||
bool got_on_header = false;
|
||||
bool got_on_body = false;
|
||||
bool got_content_length = false;
|
||||
bool got_on_prepare = false;
|
||||
bool got_on_commit = false;
|
||||
bool got_on_chunk = false;
|
||||
bool got_on_complete = false;
|
||||
|
||||
@ -50,10 +47,9 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
on_request(
|
||||
string_view const& method_,
|
||||
string_view const& path_,
|
||||
int version_, error_code& ec)
|
||||
on_request(string_view const& method_,
|
||||
string_view const& path_,
|
||||
int version_, error_code& ec)
|
||||
{
|
||||
method = std::string(
|
||||
method_.data(), method_.size());
|
||||
@ -98,19 +94,13 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
on_body(error_code& ec)
|
||||
on_body(boost::optional<
|
||||
std::uint64_t> const& content_length_,
|
||||
error_code& ec)
|
||||
{
|
||||
got_on_body = true;
|
||||
if(fc_)
|
||||
fc_->fail(ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_body(std::uint64_t content_length,
|
||||
error_code& ec)
|
||||
{
|
||||
got_on_body = true;
|
||||
got_content_length = true;
|
||||
got_content_length =
|
||||
static_cast<bool>(content_length_);
|
||||
if(fc_)
|
||||
fc_->fail(ec);
|
||||
}
|
||||
@ -120,12 +110,13 @@ public:
|
||||
error_code& ec)
|
||||
{
|
||||
body.append(s.data(), s.size());
|
||||
if(fc_)
|
||||
fc_->fail(ec);
|
||||
}
|
||||
|
||||
void
|
||||
on_chunk(std::uint64_t,
|
||||
string_view const&,
|
||||
error_code& ec)
|
||||
string_view const&, error_code& ec)
|
||||
{
|
||||
got_on_chunk = true;
|
||||
if(fc_)
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <beast/http/type_traits.hpp>
|
||||
|
||||
#include <beast/http/empty_body.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
@ -17,5 +18,7 @@ BOOST_STATIC_ASSERT(! is_body_reader<int>::value);
|
||||
|
||||
BOOST_STATIC_ASSERT(is_body_reader<empty_body>::value);
|
||||
|
||||
BOOST_STATIC_ASSERT(! is_body_writer<std::string>::value);
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
@ -382,7 +382,7 @@ public:
|
||||
m.body = "*****";
|
||||
error_code ec;
|
||||
write(fs, m, ec);
|
||||
if(ec == boost::asio::error::eof)
|
||||
if(ec == error::end_of_stream)
|
||||
{
|
||||
BEAST_EXPECT(fs.next_layer().str ==
|
||||
"GET / HTTP/1.0\r\n"
|
||||
@ -415,7 +415,7 @@ public:
|
||||
m.body = "*****";
|
||||
error_code ec;
|
||||
async_write(fs, m, do_yield[ec]);
|
||||
if(ec == boost::asio::error::eof)
|
||||
if(ec == error::end_of_stream)
|
||||
{
|
||||
BEAST_EXPECT(fs.next_layer().str ==
|
||||
"GET / HTTP/1.0\r\n"
|
||||
@ -559,7 +559,7 @@ public:
|
||||
test::string_ostream ss(ios_);
|
||||
error_code ec;
|
||||
write(ss, m, ec);
|
||||
BEAST_EXPECT(ec == boost::asio::error::eof);
|
||||
BEAST_EXPECT(ec == error::end_of_stream);
|
||||
BEAST_EXPECT(ss.str ==
|
||||
"GET / HTTP/1.0\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
@ -596,7 +596,7 @@ public:
|
||||
test::string_ostream ss(ios_);
|
||||
error_code ec;
|
||||
write(ss, m, ec);
|
||||
BEAST_EXPECT(ec == boost::asio::error::eof);
|
||||
BEAST_EXPECT(ec == error::end_of_stream);
|
||||
BEAST_EXPECT(ss.str ==
|
||||
"GET / HTTP/1.1\r\n"
|
||||
"User-Agent: test\r\n"
|
||||
@ -949,93 +949,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/** Execute a child process and return the output as an HTTP response.
|
||||
|
||||
@param input A stream to read the child process output from.
|
||||
|
||||
@param output A stream to write the HTTP response to.
|
||||
*/
|
||||
template<class SyncReadStream, class SyncWriteStream>
|
||||
void
|
||||
cgi_process(SyncReadStream& input, SyncWriteStream& output, error_code& ec)
|
||||
{
|
||||
multi_buffer b;
|
||||
message<false, buffer_body<true,
|
||||
typename multi_buffer::const_buffers_type>, fields> m;
|
||||
m.status = 200;
|
||||
m.version = 11;
|
||||
m.fields.insert("Server", "cgi-process");
|
||||
m.fields.insert("Transfer-Encoding", "chunked");
|
||||
m.body.first = boost::none;
|
||||
m.body.second = true;
|
||||
|
||||
auto sr = make_serializer(m);
|
||||
|
||||
// send the header first, so the
|
||||
// other end gets it right away
|
||||
for(;;)
|
||||
{
|
||||
write_some(output, sr, ec);
|
||||
if(ec == error::need_more)
|
||||
{
|
||||
ec = {};
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
return;
|
||||
}
|
||||
|
||||
// send the body
|
||||
for(;;)
|
||||
{
|
||||
// read from input
|
||||
auto bytes_transferred =
|
||||
input.read_some(b.prepare(1024), ec);
|
||||
if(ec == boost::asio::error::eof)
|
||||
{
|
||||
BOOST_ASSERT(bytes_transferred == 0);
|
||||
ec = {};
|
||||
m.body = {boost::none, false};
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ec)
|
||||
return;
|
||||
b.commit(bytes_transferred);
|
||||
m.body = {b.data(), true};
|
||||
}
|
||||
|
||||
// write to output
|
||||
for(;;)
|
||||
{
|
||||
write_some(output, sr, ec);
|
||||
if(ec == error::need_more)
|
||||
{
|
||||
ec = {};
|
||||
break;
|
||||
}
|
||||
if(ec)
|
||||
return;
|
||||
if(sr.is_done())
|
||||
goto is_done;
|
||||
}
|
||||
b.consume(b.size());
|
||||
}
|
||||
is_done:
|
||||
;
|
||||
}
|
||||
|
||||
void
|
||||
testCgiRelay()
|
||||
{
|
||||
error_code ec;
|
||||
std::string const body = "Hello, world!\n";
|
||||
test::string_ostream so{get_io_service(), 3};
|
||||
test::string_istream si{get_io_service(), body, 6};
|
||||
cgi_process(si, so, ec);
|
||||
BEAST_EXPECT(equal_body<false>(so.str, body));
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
yield_to([&](yield_context yield){
|
||||
@ -1046,7 +959,6 @@ public:
|
||||
test_std_ostream();
|
||||
testOstream();
|
||||
testIoService();
|
||||
testCgiRelay();
|
||||
yield_to(
|
||||
[&](yield_context yield)
|
||||
{
|
||||
|
Reference in New Issue
Block a user