I'm trying to read the chunked response body of an HTTP request asynchronously using Boost.
In my code, which I've copied from here, I have already read the headers and am now trying to read the body. This is the minimal example:
void body_read_loop() {
http::response_parser<http::buffer_body> parser_ = http::response_parser<http::buffer_body>();
async_read(*sslSocket, buf, parser_, [this](error_code ec, size_t) {
});
}
Don't mind the fact that this code wouldn't actually do anything. The problem I'm facing is the fact that compiling gives the following error:
Call to deleted constructor of 'boost::beast::http::parser<false, boost::beast::http::buffer_body>'
I don't understand this error, since the function being called captures by reference:
template<
class AsyncReadStream,
class DynamicBuffer,
bool isRequest,
BOOST_BEAST_ASYNC_TPARAM2 ReadHandler>
BOOST_BEAST_ASYNC_RESULT2(ReadHandler)
async_read(
AsyncReadStream& stream,
DynamicBuffer& buffer,
basic_parser<isRequest>& parser,
ReadHandler&& handler)
{
MCVE which, for me, doesn't compile on Ubuntu with Boost 1.74.0:
#include <boost/beast.hpp>
#include <iostream>
#include <span>
namespace net = boost::asio;
namespace beast = boost::beast;
namespace http = beast::http;
using boost::system::error_code;
using net::ip::tcp;
static void check(error_code& ec) {
if (ec && ec != http::error::need_buffer) // expected
throw boost::system::system_error(ec);
}
template <typename Derived> struct CommonImpl {
net::io_context ioc;
tcp::socket s{ioc};
tcp::resolver r{ioc};
http::request<http::empty_body> req{http::verb::get, "/stream-bytes/2000?seed=42", 11};
void run() {
r.async_resolve(
"httpbin.org", "http",
beast::bind_front_handler(&CommonImpl::on_resolved, this));
ioc.run();
}
void on_resolved(error_code ec, tcp::resolver::results_type eps) {
check(ec);
async_connect(s, eps, [this](error_code ec, tcp::endpoint) {
check(ec);
req.set(http::field::host, "httpbin.org");
async_write(s, req, beast::bind_front_handler(&CommonImpl::on_sent, this));
});
}
void on_sent(error_code ec, size_t) {
check(ec);
static_cast<Derived&>(*this).do_read_response();
}
};
struct BufferImpl : CommonImpl<BufferImpl> {
http::response_parser<http::buffer_body> p;
http::response<http::buffer_body>& res = p.get();
http::buffer_body::value_type& body_val = res.body();
beast::flat_buffer buf;
void do_read_response() {
async_read_header(s, buf, p, beast::bind_front_handler(&BufferImpl::on_headers, this));
}
void on_headers(error_code ec, size_t) {
check(ec);
assert(p.is_header_done());
std::cout << "\n---\nresponse headers: " << res.base() << std::endl;
body_read_loop();
}
size_t checksum = 0;
size_t n = 0;
std::array<uint8_t, 512> block;
void body_read_loop() {
if (!p.is_done()) {
body_val.data = block.data();
body_val.size = block.size();
async_read(s, buf, p, [this](error_code ec, size_t) {
check(ec);
size_t decoded = block.size() - body_val.size;
n += decoded;
std::cout << "parsed " << decoded << " body bytes\n";
for (auto b : std::span(block).first(decoded))
checksum ^= b;
body_read_loop();
});
} else {
// fmt::print("buffer body, {} bytes streaming decoded, chunked? {}\n", n, p.chunked());
// fmt::print("buffer body checksum: {:#0x}\n", checksum);
}
}
};
int main(int argc, char **argv) {
BufferImpl{}.run();
return 0;
}