+ 3

How can we respond to HTTP request with raw binary data from memory using Boost and Beast?

I'm adding an HTTP server to a c++ application to help other clients communicate with it. I created a few API's that respond with JSON but I want to add an API that responds with raw binary data from dynamically computed images. This could work if I save the image to the file system and then read it from there but I want better performance. I want to skip writing and reading the file system since I already have the content in RAM. I can send content from a string like this where resultContent is dynamically generated JSON-formatted string: std::string resultContent = handleAPIGetRequest(req.target()); http::file_body::value_type body; auto const size = resultContent.size(); http::response<http::string_body> res{ http::status::ok, req.version() }; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, mime_type(".json")); res.content_length(size); res.body() = resultContent; res.keep_alive(req.keep_alive()); return send(std::move(res)); Here is how a file is sent. This can send the raw image data from a PNG file but it requires having a file in the file system: beast::error_code ec; http::file_body::value_type body; body.open(path.c_str(), beast::file_mode::scan, ec); http::response<http::file_body> res{ std::piecewise_construct, std::make_tuple(std::move(body)), std::make_tuple(http::status::ok, req.version())}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, mime_type(path)); res.content_length(size); res.keep_alive(req.keep_alive()); return send(std::move(res));

5th Jul 2021, 3:32 PM
Josh Greig
Josh Greig - avatar
2 Answers
+ 2
Josh Greig I'm assuming your dynamically computed image is available as a stream. If so, you could read bytes from that stream into a buffer: ---- char buffer[2048]; auto bytes_read = inputStream.read_some( boost::asio::buffer(buffer, sizeof(buffer)), error_codes); -- Set values to your response body and write to stream: ---- res.body().data = buffer; res.body().size = bytes_read; res.body().more = true; write(outputStream, res_serializer, error_codes); -- This snippet was pulled from an example of code that reads a chunk of bytes, sends chunks, then reads more, and continues until reaching the end. Search for the text: "example_http_send_cgi_response" to find this same code in the link below: https://www.boost.org/doc/libs/develop/libs/beast/example/doc/http_examples.hpp Hopefully, this gets you closer to what you need to figure out.
5th Jul 2021, 5:54 PM
David Carroll
David Carroll - avatar
+ 1
Thanks, David. That worked well. I used your pointers and changed to this and it works now: std::string mime; std::vector<unsigned char> resultContent = handleAPIGetBinaryRequest(req.target(), mime); char *buffer = new char[resultContent.size()]; for (unsigned int i = 0; i < resultContent.size(); i++) { buffer[i] = resultContent[i]; } beast::error_code ec; auto const size = resultContent.size(); http::response<http::buffer_body> res; res.result(http::status::ok); res.version(req.version()); res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, mime); res.content_length(size); res.body().data = buffer; res.body().size = size; res.body().more = true; res.keep_alive(req
5th Jul 2021, 7:25 PM
Josh Greig
Josh Greig - avatar