diff --git a/BUILD.md b/BUILD.md index 590a7129863..0b9ef40e61b 100644 --- a/BUILD.md +++ b/BUILD.md @@ -107,6 +107,20 @@ then you will need to choose the `libstdc++11` ABI: conan profile update settings.compiler.libcxx=libstdc++11 default ``` + +Ensure inter-operability between `boost::string_view` and `std::string_view` types: + +``` +conan profile update 'conf.tools.build:cxxflags+=["-DBOOST_BEAST_USE_STD_STRING_VIEW"]' default +conan profile update 'env.CXXFLAGS="-DBOOST_BEAST_USE_STD_STRING_VIEW"' default +``` + +If you have other flags in the `conf.tools.build` or `env.CXXFLAGS` sections, make sure to retain the existing flags and append the new ones. You can check them with: +``` +conan profile show default +``` + + **Windows** developers may need to use the x64 native build tools. An easy way to do that is to run the shortcut "x64 Native Tools Command Prompt" for the version of Visual Studio that you have installed. diff --git a/src/ripple/app/misc/ValidatorList.h b/src/ripple/app/misc/ValidatorList.h index 280818abd35..4d792b36b0f 100644 --- a/src/ripple/app/misc/ValidatorList.h +++ b/src/ripple/app/misc/ValidatorList.h @@ -633,7 +633,7 @@ class ValidatorList */ std::optional getAvailable( - boost::beast::string_view const& pubKey, + std::string_view pubKey, std::optional forceVersion = {}); /** Return the number of configured validator list sites. */ diff --git a/src/ripple/app/misc/impl/ValidatorList.cpp b/src/ripple/app/misc/impl/ValidatorList.cpp index ff5fbd90eac..13dcd7873db 100644 --- a/src/ripple/app/misc/impl/ValidatorList.cpp +++ b/src/ripple/app/misc/impl/ValidatorList.cpp @@ -34,7 +34,6 @@ #include #include -#include #include #include @@ -215,7 +214,8 @@ ValidatorList::load( return false; } - auto const id = parseBase58(TokenType::NodePublic, match[1]); + auto const id = + parseBase58(TokenType::NodePublic, match[1].str()); if (!id) { @@ -1707,7 +1707,7 @@ ValidatorList::for_each_available( std::optional ValidatorList::getAvailable( - boost::beast::string_view const& pubKey, + std::string_view pubKey, std::optional forceVersion /* = {} */) { std::shared_lock read_lock{mutex_}; diff --git a/src/ripple/basics/StringUtilities.h b/src/ripple/basics/StringUtilities.h index 8af81a37403..69314ce096e 100644 --- a/src/ripple/basics/StringUtilities.h +++ b/src/ripple/basics/StringUtilities.h @@ -110,7 +110,7 @@ strUnHex(std::string const& strSrc) } inline std::optional -strViewUnHex(boost::string_view const& strSrc) +strViewUnHex(std::string_view strSrc) { return strUnHex(strSrc.size(), strSrc.cbegin(), strSrc.cend()); } @@ -150,7 +150,7 @@ to_uint64(std::string const& s); doesn't check whether the TLD is valid. */ bool -isProperlyFormedTomlDomain(std::string const& domain); +isProperlyFormedTomlDomain(std::string_view domain); } // namespace ripple diff --git a/src/ripple/basics/base64.h b/src/ripple/basics/base64.h index 05a61133f83..515a7584e56 100644 --- a/src/ripple/basics/base64.h +++ b/src/ripple/basics/base64.h @@ -73,7 +73,7 @@ base64_encode(std::string const& s) } std::string -base64_decode(std::string const& data); +base64_decode(std::string_view data); } // namespace ripple diff --git a/src/ripple/basics/impl/StringUtilities.cpp b/src/ripple/basics/impl/StringUtilities.cpp index bebbe1ef80b..67344b48013 100644 --- a/src/ripple/basics/impl/StringUtilities.cpp +++ b/src/ripple/basics/impl/StringUtilities.cpp @@ -120,7 +120,7 @@ to_uint64(std::string const& s) } bool -isProperlyFormedTomlDomain(std::string const& domain) +isProperlyFormedTomlDomain(std::string_view domain) { // The domain must be between 4 and 128 characters long if (domain.size() < 4 || domain.size() > 128) @@ -143,7 +143,7 @@ isProperlyFormedTomlDomain(std::string const& domain) , boost::regex_constants::optimize); - return boost::regex_match(domain, re); + return boost::regex_match(domain.begin(), domain.end(), re); } } // namespace ripple diff --git a/src/ripple/basics/impl/base64.cpp b/src/ripple/basics/impl/base64.cpp index 39b615100e5..4291a99556b 100644 --- a/src/ripple/basics/impl/base64.cpp +++ b/src/ripple/basics/impl/base64.cpp @@ -242,7 +242,7 @@ base64_encode(std::uint8_t const* data, std::size_t len) } std::string -base64_decode(std::string const& data) +base64_decode(std::string_view data) { std::string dest; dest.resize(base64::decoded_size(data.size())); diff --git a/src/ripple/beast/core/LexicalCast.h b/src/ripple/beast/core/LexicalCast.h index f4c78341b91..e0fa24ca9f5 100644 --- a/src/ripple/beast/core/LexicalCast.h +++ b/src/ripple/beast/core/LexicalCast.h @@ -20,6 +20,7 @@ #ifndef BEAST_MODULE_CORE_TEXT_LEXICALCAST_H_INCLUDED #define BEAST_MODULE_CORE_TEXT_LEXICALCAST_H_INCLUDED +#include #include #include #include @@ -64,9 +65,9 @@ struct LexicalCast } }; -// Parse std::string to number -template -struct LexicalCast +// Parse a std::string_view into a number +template +struct LexicalCast { explicit LexicalCast() = default; @@ -78,7 +79,7 @@ struct LexicalCast std::enable_if_t< std::is_integral_v && !std::is_same_v, bool> - operator()(Integral& out, std::string const& in) const + operator()(Integral& out, std::string_view in) const { auto first = in.data(); auto last = in.data() + in.size(); @@ -92,20 +93,23 @@ struct LexicalCast } bool - operator()(bool& out, std::string in) const + operator()(bool& out, std::string_view in) const { + std::string result; + // Convert the input to lowercase - std::transform(in.begin(), in.end(), in.begin(), [](auto c) { - return std::tolower(static_cast(c)); - }); + std::transform( + in.begin(), in.end(), std::back_inserter(result), [](auto c) { + return std::tolower(static_cast(c)); + }); - if (in == "1" || in == "true") + if (result == "1" || result == "true") { out = true; return true; } - if (in == "0" || in == "false") + if (result == "0" || result == "false") { out = false; return true; @@ -114,9 +118,38 @@ struct LexicalCast return false; } }; - //------------------------------------------------------------------------------ +// Parse boost library's string_view to number or boolean value +// Note: As of Jan 2024, Boost contains three different types of string_view +// (boost::core::basic_string_view, boost::string_ref and +// boost::string_view). The below template specialization is included because +// it is used in the handshake.cpp file +template +struct LexicalCast> +{ + explicit LexicalCast() = default; + + bool + operator()(Out& out, boost::core::basic_string_view in) const + { + return LexicalCast()(out, in); + } +}; + +// Parse std::string to number or boolean value +template +struct LexicalCast +{ + explicit LexicalCast() = default; + + bool + operator()(Out& out, std::string in) const + { + return LexicalCast()(out, in); + } +}; + // Conversion from null terminated char const* template struct LexicalCast @@ -126,7 +159,8 @@ struct LexicalCast bool operator()(Out& out, char const* in) const { - return LexicalCast()(out, in); + assert(in); + return LexicalCast()(out, in); } }; @@ -140,7 +174,8 @@ struct LexicalCast bool operator()(Out& out, char* in) const { - return LexicalCast()(out, in); + assert(in); + return LexicalCast()(out, in); } }; diff --git a/src/ripple/json/Output.h b/src/ripple/json/Output.h index 74aaa65269d..96905c20ba9 100644 --- a/src/ripple/json/Output.h +++ b/src/ripple/json/Output.h @@ -22,6 +22,7 @@ #include #include +#include namespace Json { diff --git a/src/ripple/overlay/impl/Cluster.cpp b/src/ripple/overlay/impl/Cluster.cpp index 53ec13b7403..a7b1e44d4b7 100644 --- a/src/ripple/overlay/impl/Cluster.cpp +++ b/src/ripple/overlay/impl/Cluster.cpp @@ -17,7 +17,6 @@ */ //============================================================================== -#include #include #include #include @@ -27,7 +26,6 @@ #include #include #include -#include namespace ripple { @@ -113,7 +111,8 @@ Cluster::load(Section const& nodes) return false; } - auto const id = parseBase58(TokenType::NodePublic, match[1]); + auto const id = + parseBase58(TokenType::NodePublic, match[1].str()); if (!id) { diff --git a/src/ripple/overlay/impl/Handshake.cpp b/src/ripple/overlay/impl/Handshake.cpp index 8fe383e1d47..b655859dcbc 100644 --- a/src/ripple/overlay/impl/Handshake.cpp +++ b/src/ripple/overlay/impl/Handshake.cpp @@ -20,16 +20,12 @@ #include #include #include -#include #include #include #include #include - #include - #include -#include // VFALCO Shouldn't we have to include the OpenSSL // headers or something for SSL_get_finished? @@ -46,8 +42,8 @@ getFeatureValue( return {}; boost::smatch match; boost::regex rx(feature + "=([^;\\s]+)"); - std::string const value = header->value(); - if (boost::regex_search(value, match, rx)) + std::string const allFeatures(header->value()); + if (boost::regex_search(allFeatures, match, rx)) return {match[1]}; return {}; } @@ -243,7 +239,7 @@ verifyHandshake( { std::uint32_t nid; - if (!beast::lexicalCastChecked(nid, std::string(iter->value()))) + if (!beast::lexicalCastChecked(nid, iter->value())) throw std::runtime_error("Invalid peer network identifier"); if (networkID && nid != *networkID) @@ -252,8 +248,7 @@ verifyHandshake( if (auto const iter = headers.find("Network-Time"); iter != headers.end()) { - auto const netTime = - [str = std::string(iter->value())]() -> TimeKeeper::time_point { + auto const netTime = [str = iter->value()]() -> TimeKeeper::time_point { TimeKeeper::duration::rep val; if (beast::lexicalCastChecked(val, str)) diff --git a/src/ripple/overlay/impl/OverlayImpl.cpp b/src/ripple/overlay/impl/OverlayImpl.cpp index 1bb9a381edd..7e60b2c120c 100644 --- a/src/ripple/overlay/impl/OverlayImpl.cpp +++ b/src/ripple/overlay/impl/OverlayImpl.cpp @@ -39,7 +39,6 @@ #include #include -#include namespace ripple { @@ -826,7 +825,7 @@ OverlayImpl::getOverlayInfo() auto version{sp->getVersion()}; if (!version.empty()) // Could move here if Json::value supported moving from strings - pv[jss::version] = version; + pv[jss::version] = std::string{version}; } std::uint32_t minSeq, maxSeq; @@ -994,9 +993,9 @@ OverlayImpl::processValidatorList( return true; }; - auto key = req.target().substr(prefix.size()); + std::string_view key = req.target().substr(prefix.size()); - if (auto slash = key.find('/'); slash != boost::string_view::npos) + if (auto slash = key.find('/'); slash != std::string_view::npos) { auto verString = key.substr(0, slash); if (!boost::conversion::try_lexical_convert(verString, version)) diff --git a/src/ripple/overlay/impl/PeerImp.cpp b/src/ripple/overlay/impl/PeerImp.cpp index f93c9f135ad..4cd3a89452d 100644 --- a/src/ripple/overlay/impl/PeerImp.cpp +++ b/src/ripple/overlay/impl/PeerImp.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -41,7 +40,6 @@ #include #include -#include #include #include @@ -160,7 +158,7 @@ PeerImp::run() return post(strand_, std::bind(&PeerImp::run, shared_from_this())); auto parseLedgerHash = - [](std::string const& value) -> std::optional { + [](std::string_view value) -> std::optional { if (uint256 ret; ret.parseHex(value)) return ret; @@ -397,15 +395,15 @@ PeerImp::json() } if (auto const d = domain(); !d.empty()) - ret[jss::server_domain] = domain(); + ret[jss::server_domain] = std::string{d}; if (auto const nid = headers_["Network-ID"]; !nid.empty()) - ret[jss::network_id] = std::string(nid); + ret[jss::network_id] = std::string{nid}; ret[jss::load] = usage_.balance(); if (auto const version = getVersion(); !version.empty()) - ret[jss::version] = version; + ret[jss::version] = std::string{version}; ret[jss::protocol] = to_string(protocol_); diff --git a/src/ripple/resource/ResourceManager.h b/src/ripple/resource/ResourceManager.h index 7471a37ab35..10e20a82f06 100644 --- a/src/ripple/resource/ResourceManager.h +++ b/src/ripple/resource/ResourceManager.h @@ -49,7 +49,7 @@ class Manager : public beast::PropertyStream::Source newInboundEndpoint( beast::IP::Endpoint const& address, bool const proxy, - boost::string_view const& forwardedFor) = 0; + std::string_view forwardedFor) = 0; /** Create a new endpoint keyed by outbound IP address and port. */ virtual Consumer diff --git a/src/ripple/resource/impl/ResourceManager.cpp b/src/ripple/resource/impl/ResourceManager.cpp index 1a7e74ec1f8..ae4eaac23fd 100644 --- a/src/ripple/resource/impl/ResourceManager.cpp +++ b/src/ripple/resource/impl/ResourceManager.cpp @@ -77,14 +77,13 @@ class ManagerImp : public Manager newInboundEndpoint( beast::IP::Endpoint const& address, bool const proxy, - boost::string_view const& forwardedFor) override + std::string_view forwardedFor) override { if (!proxy) return newInboundEndpoint(address); boost::system::error_code ec; - auto const proxiedIp = - boost::asio::ip::make_address(forwardedFor.to_string(), ec); + auto const proxiedIp = boost::asio::ip::make_address(forwardedFor, ec); if (ec) { journal_.warn() diff --git a/src/ripple/rpc/Context.h b/src/ripple/rpc/Context.h index 7a22ed9fe0c..ea18efcec6c 100644 --- a/src/ripple/rpc/Context.h +++ b/src/ripple/rpc/Context.h @@ -57,8 +57,8 @@ struct JsonContext : public Context */ struct Headers { - boost::string_view user; - boost::string_view forwardedFor; + std::string_view user; + std::string_view forwardedFor; }; Json::Value params; diff --git a/src/ripple/rpc/Role.h b/src/ripple/rpc/Role.h index c4f1f730c02..1ce76f90d6d 100644 --- a/src/ripple/rpc/Role.h +++ b/src/ripple/rpc/Role.h @@ -56,15 +56,15 @@ requestRole( Port const& port, Json::Value const& params, beast::IP::Endpoint const& remoteIp, - boost::string_view const& user); + std::string_view user); Resource::Consumer requestInboundEndpoint( Resource::Manager& manager, beast::IP::Endpoint const& remoteAddress, Role const& role, - boost::string_view const& user, - boost::string_view const& forwardedFor); + std::string_view user, + std::string_view forwardedFor); /** * Check if the role entitles the user to unlimited resources. @@ -85,7 +85,7 @@ ipAllowed( std::vector const& nets4, std::vector const& nets6); -boost::string_view +std::string_view forwardedFor(http_request_type const& request); } // namespace ripple diff --git a/src/ripple/rpc/ServerHandler.h b/src/ripple/rpc/ServerHandler.h index 07fb61362a0..7e9e427cfdb 100644 --- a/src/ripple/rpc/ServerHandler.h +++ b/src/ripple/rpc/ServerHandler.h @@ -208,8 +208,8 @@ class ServerHandler beast::IP::Endpoint const& remoteIPAddress, Output&&, std::shared_ptr coro, - boost::string_view forwardedFor, - boost::string_view user); + std::string_view forwardedFor, + std::string_view user); Handoff statusResponse(http_request_type const& request) const; diff --git a/src/ripple/rpc/handlers/Ping.cpp b/src/ripple/rpc/handlers/Ping.cpp index 7bd91d8edc1..efe9063a1e2 100644 --- a/src/ripple/rpc/handlers/Ping.cpp +++ b/src/ripple/rpc/handlers/Ping.cpp @@ -39,13 +39,13 @@ doPing(RPC::JsonContext& context) break; case Role::IDENTIFIED: ret[jss::role] = "identified"; - ret[jss::username] = context.headers.user.to_string(); + ret[jss::username] = std::string{context.headers.user}; if (context.headers.forwardedFor.size()) - ret[jss::ip] = context.headers.forwardedFor.to_string(); + ret[jss::ip] = std::string{context.headers.forwardedFor}; break; case Role::PROXY: ret[jss::role] = "proxied"; - ret[jss::ip] = context.headers.forwardedFor.to_string(); + ret[jss::ip] = std::string{context.headers.forwardedFor}; default:; } diff --git a/src/ripple/rpc/impl/Role.cpp b/src/ripple/rpc/impl/Role.cpp index 1807ecc20f6..54f6d6e2575 100644 --- a/src/ripple/rpc/impl/Role.cpp +++ b/src/ripple/rpc/impl/Role.cpp @@ -18,9 +18,7 @@ //============================================================================== #include -#include #include -#include #include #include #include @@ -96,7 +94,7 @@ requestRole( Port const& port, Json::Value const& params, beast::IP::Endpoint const& remoteIp, - boost::string_view const& user) + std::string_view user) { if (isAdmin(port, params, remoteIp.address())) return Role::ADMIN; @@ -142,8 +140,8 @@ requestInboundEndpoint( Resource::Manager& manager, beast::IP::Endpoint const& remoteAddress, Role const& role, - boost::string_view const& user, - boost::string_view const& forwardedFor) + std::string_view user, + std::string_view forwardedFor) { if (isUnlimited(role)) return manager.newUnlimitedEndpoint(remoteAddress); @@ -152,18 +150,18 @@ requestInboundEndpoint( remoteAddress, role == Role::PROXY, forwardedFor); } -static boost::string_view -extractIpAddrFromField(boost::string_view field) +static std::string_view +extractIpAddrFromField(std::string_view field) { // Lambda to trim leading and trailing spaces on the field. - auto trim = [](boost::string_view str) -> boost::string_view { - boost::string_view ret = str; + auto trim = [](std::string_view str) -> std::string_view { + std::string_view ret = str; // Only do the work if there's at least one leading space. if (!ret.empty() && ret.front() == ' ') { std::size_t const firstNonSpace = ret.find_first_not_of(' '); - if (firstNonSpace == boost::string_view::npos) + if (firstNonSpace == std::string_view::npos) // We know there's at least one leading space. So if we got // npos, then it must be all spaces. Return empty string_view. return {}; @@ -178,7 +176,7 @@ extractIpAddrFromField(boost::string_view field) c == ' ' || c == '\r' || c == '\n') { std::size_t const lastNonSpace = ret.find_last_not_of(" \r\n"); - if (lastNonSpace == boost::string_view::npos) + if (lastNonSpace == std::string_view::npos) // We know there's at least one leading space. So if we // got npos, then it must be all spaces. return {}; @@ -189,7 +187,7 @@ extractIpAddrFromField(boost::string_view field) return ret; }; - boost::string_view ret = trim(field); + std::string_view ret = trim(field); if (ret.empty()) return {}; @@ -251,13 +249,13 @@ extractIpAddrFromField(boost::string_view field) // If there's a port appended to the IP address, strip that by // terminating at the colon. - if (std::size_t colon = ret.find(':'); colon != boost::string_view::npos) + if (std::size_t colon = ret.find(':'); colon != std::string_view::npos) ret = ret.substr(0, colon); return ret; } -boost::string_view +std::string_view forwardedFor(http_request_type const& request) { // Look for the Forwarded field in the request. @@ -286,10 +284,9 @@ forwardedFor(http_request_type const& request) // We found a "for=". Scan for the end of the IP address. std::size_t const pos = [&found, &it]() { - std::size_t pos = - boost::string_view(found, it->value().end() - found) - .find_first_of(",;"); - if (pos != boost::string_view::npos) + std::size_t pos = std::string_view(found, it->value().end() - found) + .find_first_of(",;"); + if (pos != std::string_view::npos) return pos; return it->value().size() - forStr.size(); diff --git a/src/ripple/rpc/impl/ServerHandler.cpp b/src/ripple/rpc/impl/ServerHandler.cpp index d643bdb6331..700e1c64976 100644 --- a/src/ripple/rpc/impl/ServerHandler.cpp +++ b/src/ripple/rpc/impl/ServerHandler.cpp @@ -247,6 +247,8 @@ build_map(boost::beast::http::fields const& h) std::map c; for (auto const& e : h) { + // key cannot be a std::string_view because it needs to be used in + // map and along with iterators std::string key(e.name_string()); std::transform(key.begin(), key.end(), key.begin(), [](auto kc) { return std::tolower(static_cast(kc)); @@ -592,8 +594,8 @@ ServerHandler::processRequest( beast::IP::Endpoint const& remoteIPAddress, Output&& output, std::shared_ptr coro, - boost::string_view forwardedFor, - boost::string_view user) + std::string_view forwardedFor, + std::string_view user) { auto rpcJ = app_.journal("RPC"); @@ -847,8 +849,8 @@ ServerHandler::processRequest( */ if (role != Role::IDENTIFIED && role != Role::PROXY) { - forwardedFor.clear(); - user.clear(); + forwardedFor.remove_suffix(forwardedFor.size()); + user.remove_suffix(user.size()); } JLOG(m_journal.debug()) << "Query: " << strMethod << params; diff --git a/src/ripple/rpc/impl/WSInfoSub.h b/src/ripple/rpc/impl/WSInfoSub.h index 267c8f98f24..31f6353b994 100644 --- a/src/ripple/rpc/impl/WSInfoSub.h +++ b/src/ripple/rpc/impl/WSInfoSub.h @@ -55,13 +55,13 @@ class WSInfoSub : public InfoSub } } - boost::string_view + std::string_view user() const { return user_; } - boost::string_view + std::string_view forwarded_for() const { return fwdfor_; diff --git a/src/test/app/NFTokenDir_test.cpp b/src/test/app/NFTokenDir_test.cpp index e9addfa83f7..8e6a9fe5209 100644 --- a/src/test/app/NFTokenDir_test.cpp +++ b/src/test/app/NFTokenDir_test.cpp @@ -185,7 +185,7 @@ class NFTokenDir_test : public beast::unit_test::suite // Create accounts for all of the seeds and fund those accounts. std::vector accounts; accounts.reserve(seeds.size()); - for (std::string_view const& seed : seeds) + for (std::string_view seed : seeds) { Account const& account = accounts.emplace_back( Account::base58Seed, std::string(seed)); @@ -409,7 +409,7 @@ class NFTokenDir_test : public beast::unit_test::suite // Create accounts for all of the seeds and fund those accounts. std::vector accounts; accounts.reserve(seeds.size()); - for (std::string_view const& seed : seeds) + for (std::string_view seed : seeds) { Account const& account = accounts.emplace_back( Account::base58Seed, std::string(seed)); @@ -659,7 +659,7 @@ class NFTokenDir_test : public beast::unit_test::suite // Create accounts for all of the seeds and fund those accounts. std::vector accounts; accounts.reserve(seeds.size()); - for (std::string_view const& seed : seeds) + for (std::string_view seed : seeds) { Account const& account = accounts.emplace_back(Account::base58Seed, std::string(seed)); @@ -840,7 +840,7 @@ class NFTokenDir_test : public beast::unit_test::suite // Create accounts for all of the seeds and fund those accounts. std::vector accounts; accounts.reserve(seeds.size()); - for (std::string_view const& seed : seeds) + for (std::string_view seed : seeds) { Account const& account = accounts.emplace_back(Account::base58Seed, std::string(seed)); diff --git a/src/test/jtx/TrustedPublisherServer.h b/src/test/jtx/TrustedPublisherServer.h index da7ea1c137a..db57b2d94c6 100644 --- a/src/test/jtx/TrustedPublisherServer.h +++ b/src/test/jtx/TrustedPublisherServer.h @@ -574,7 +574,7 @@ xbEQ+TUZ5jbJGSeBqNFKFeuOUQGJ46Io0jBSYd4rSmKUXkvElQwR+n7KF3jy1uAt if (ec) break; - std::string path = req.target(); + std::string_view const path = req.target(); res.insert("Server", "TrustedPublisherServer"); res.version(req.version()); res.keep_alive(req.keep_alive()); @@ -677,7 +677,9 @@ xbEQ+TUZ5jbJGSeBqNFKFeuOUQGJ46Io0jBSYd4rSmKUXkvElQwR+n7KF3jy1uAt // unknown request res.result(boost::beast::http::status::not_found); res.insert("Content-Type", "text/html"); - res.body() = "The file '" + path + "' was not found"; + res.body() = "The file '" + std::string(path) + + "' was not " + "found"; } if (prepare)