Read boost chunked response body

51 views Asked by At

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; 
}
0

There are 0 answers