Search code examples
c++boostboost-asioboost-beast

How to construct a boost::beast::http::message from std::string?


Is that possible to construct a boost::beast::http::message (specifically I have to construct a boost::beast::http::response<bb_http::string_body>) from std::string, std::string_view or other raw buffer?

Maybe there is some kind of parser? From what I see in Boost.Beast samples, we can either:

  1. receive a response from boost::beast::read* functions. In that case the first argument should be a SyncReadStream, which has to comply with contracts of SyncReadStream from boost/beast/core/type_traits.hpp:
struct is_sync_read_stream<T, detail::void_t<decltype(
    std::declval<std::size_t&>() = std::declval<T>().read_some(
        std::declval<detail::MutableBufferSequence>()),
    std::declval<std::size_t&>() = std::declval<T>().read_some(
        std::declval<detail::MutableBufferSequence>(),
        std::declval<boost::system::error_code&>()),
            (void)0)>> : std::true_type {};
  1. or construct it by hand like http::request<http::string_body> req{http::verb::get, target, version};

Solution

  • You can manually invoke the parser, e.g. with this simple skeleton function:

    http::response<http::string_body> do_parse(std::string_view input)
    {
        beast::error_code ec;
        http::response_parser<http::string_body> p;
    
        // read headers
        auto buf = boost::asio::buffer(sample);
        auto n = p.put(buf, ec);
        assert(p.is_header_done());
    
        // read body
        if (!ec) {
            buf += n;
            n = p.put(buf, ec);
            p.put_eof(ec);
        }
        if (ec)
            throw boost::system::system_error(ec);
        assert(p.is_done());
    
        return p.release();
    }
    

    This assumes that input is a sinlge complete request.

    Live Demo

    Live On Coliru

    #include <boost/beast.hpp>
    #include <boost/beast/http.hpp>
    #include <string_view>
    #include <iostream>
    #include <iomanip>
    namespace beast = boost::beast;
    namespace http = beast::http;
    
    http::response<http::string_body> do_parse(std::string_view input)
    {
        beast::error_code ec;
        http::response_parser<http::string_body> p;
    
        // read headers
        auto buf = boost::asio::buffer(input);
        auto n   = p.put(buf, ec);
        assert(p.is_header_done());
    
        // read body
        if (!ec) {
            buf += n;
            n = p.put(buf, ec);
            p.put_eof(ec);
        }
        if (ec)
            throw boost::system::system_error(ec);
        assert(p.is_done());
    
        return p.release();
    }
    
    int main() {
        auto res = do_parse(
            "HTTP/1.1 200 OK\r\n"
            "Date: Sun, 10 Oct 2010 23:26:07 GMT\r\n"
            "Server: Apache/2.2.8 (Ubuntu) mod_ssl/2.2.8 OpenSSL/0.9.8g\r\n"
            "Last-Modified: Sun, 26 Sep 2010 22:04:35 GMT\r\n"
            "ETag: 45b6-834-49130cc1182c0\r\n"
            "Accept-Ranges: bytes\r\n"
            "Content-Length: 12\r\n"
            "Connection: close\r\n"
            "Content-Type: text/html\r\n"
            "\r\n"
            "Hello world!");
        std::cout << res << '\n';
        std::cout << "====== body:\n" << std::quoted(res.body()) << "\n";
    }
    

    Prints

    HTTP/1.1 200 OK
    Date: Sun, 10 Oct 2010 23:26:07 GMT
    Server: Apache/2.2.8 (Ubuntu) mod_ssl/2.2.8 OpenSSL/0.9.8g
    Last-Modified: Sun, 26 Sep 2010 22:04:35 GMT
    ETag: 45b6-834-49130cc1182c0
    Accept-Ranges: bytes
    Content-Length: 12
    Connection: close
    Content-Type: text/html
    Hello world!
    ====== body:
    "Hello world!"