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