Search code examples
c++boostbeast

string_view points to another string


I'm using Boost 1.70 and found a rather puzzling issue where sometimes boost::string_view seems to point to another string.

This is the function call where a boost::string_view is created as follows:

boost::beast::string_view message = "The resource '" + target.to_string() + "' was not found.";
return prepareError(req, message, beast_http::status::not_found);

And this is the prepareError() method which gets called where something really odd happens. The fist version uses message.to_string() while constructing a string variable:

    template<class Body, class Allocator>
    beast_http::response<beast_http::string_body> prepareError(beast_http::request<Body, beast_http::basic_fields<Allocator>>& req,
        boost::beast::string_view message, beast_http::status statusCode) {
        beast_http::response<beast_http::string_body> res{statusCode, req.version()};
        res.set(beast_http::field::server, SERVER_VERSION_STRING);
        res.set(beast_http::field::content_type, MIMETYPE_JSON);
        if (_allowCrossOrigin) {
            res.set(beast_http::field::access_control_allow_origin, "*");
        }
        res.keep_alive(req.keep_alive());
        int sc = (int)statusCode;
        std::string json = std::string("{\n  error_code:") +
                            std::to_string(sc) + std::string(",\n") +
                            std::string("  error_description:\"") +
                            message.to_string() + std::string("\"\n}");

        res.body() = json;
        res.prepare_payload();
        return res;
    } 

This json variable will contain the following string for example:

Printing description of json:
(std::__1::string) json = "{\n  error_code:404,\n  error_description:\"{\n  error_code:404,\n  error_descript\"\n}"

This is really odd. In the debugger the message variable contains an empty string. And as you can see from the method call this should not be the expected result for that string.

This is the same method with the only difference that there is a temporary assignment of the message variable, which is a string_view, to a std::string.

    template<class Body, class Allocator>
    beast_http::response<beast_http::string_body> prepareError(beast_http::request<Body, beast_http::basic_fields<Allocator>>& req,
        boost::beast::string_view message, beast_http::status statusCode) {
        beast_http::response<beast_http::string_body> res{statusCode, req.version()};
        res.set(beast_http::field::server, SERVER_VERSION_STRING);
        res.set(beast_http::field::content_type, MIMETYPE_JSON);
        if (_allowCrossOrigin) {
            res.set(beast_http::field::access_control_allow_origin, "*");
        }
        res.keep_alive(req.keep_alive());
        int sc = (int)statusCode;
        std::string msg = message.to_string();
        std::string json = std::string("{\n  error_code:") +
                            std::to_string(sc) + std::string(",\n") +
                            std::string("  error_description:\"") +
                            msg + std::string("\"\n}");

        res.body() = json;
        res.prepare_payload();
        return res;
    }

This gives the expected string:

Printing description of json:
(std::__1::string) json = "{\n  error_code:404,\n  error_description:\"The resource '/zones' was not found.\"\n}"

Solution

  • Your error comes from

    boost::beast::string_view message = "The resource '" + target.to_string() + "' was not found.";
    

    You create a temporary string on the right hand side, and set the view to point to it. After the line ends, that temporary string is destroyed and your view now points to invalid memory.

    What you need to do is pass the temporary directly to the function, like this:

    return prepareError(req, "The resource '" + target.to_string() + "' was not found.", beast_http::status::not_found);
    

    Or, capture the result into a std::string variable, and then pass that to the function.