104e438cbSEd Tanous #pragma once 204e438cbSEd Tanous #include "logging.hpp" 33ccb3adbSEd Tanous #include "utils/hex_utils.hpp" 404e438cbSEd Tanous 504e438cbSEd Tanous #include <boost/beast/http/message.hpp> 6d4b6c660SEd Tanous #include <boost/beast/http/string_body.hpp> 7faf100f9SEd Tanous #include <nlohmann/json.hpp> 804e438cbSEd Tanous 98a9a25c8SEd Tanous #include <optional> 1004e438cbSEd Tanous #include <string> 118a9a25c8SEd Tanous #include <string_view> 1204e438cbSEd Tanous 1304e438cbSEd Tanous namespace crow 1404e438cbSEd Tanous { 1504e438cbSEd Tanous 1604e438cbSEd Tanous template <typename Adaptor, typename Handler> 1704e438cbSEd Tanous class Connection; 1804e438cbSEd Tanous 1904e438cbSEd Tanous struct Response 2004e438cbSEd Tanous { 2104e438cbSEd Tanous template <typename Adaptor, typename Handler> 2204e438cbSEd Tanous friend class crow::Connection; 2304e438cbSEd Tanous using response_type = 2404e438cbSEd Tanous boost::beast::http::response<boost::beast::http::string_body>; 2504e438cbSEd Tanous 2604e438cbSEd Tanous std::optional<response_type> stringResponse; 2704e438cbSEd Tanous 2804e438cbSEd Tanous nlohmann::json jsonValue; 2904e438cbSEd Tanous 3026ccae32SEd Tanous void addHeader(std::string_view key, std::string_view value) 3104e438cbSEd Tanous { 32994fd86aSEd Tanous stringResponse->insert(key, value); 3304e438cbSEd Tanous } 3404e438cbSEd Tanous 3504e438cbSEd Tanous void addHeader(boost::beast::http::field key, std::string_view value) 3604e438cbSEd Tanous { 37994fd86aSEd Tanous stringResponse->insert(key, value); 38994fd86aSEd Tanous } 39994fd86aSEd Tanous 40994fd86aSEd Tanous void clearHeader(boost::beast::http::field key) 41994fd86aSEd Tanous { 42994fd86aSEd Tanous stringResponse->erase(key); 4304e438cbSEd Tanous } 4404e438cbSEd Tanous 4589492a15SPatrick Williams Response() : stringResponse(response_type{}) {} 4604e438cbSEd Tanous 4713548d85SEd Tanous Response(Response&& res) noexcept : 48f8fe53e7SEd Tanous stringResponse(std::move(res.stringResponse)), 49f8fe53e7SEd Tanous jsonValue(std::move(res.jsonValue)), completed(res.completed) 5013548d85SEd Tanous { 5113548d85SEd Tanous // See note in operator= move handler for why this is needed. 5213548d85SEd Tanous if (!res.completed) 5313548d85SEd Tanous { 5413548d85SEd Tanous completeRequestHandler = std::move(res.completeRequestHandler); 5513548d85SEd Tanous res.completeRequestHandler = nullptr; 5613548d85SEd Tanous } 5713548d85SEd Tanous isAliveHelper = res.isAliveHelper; 5813548d85SEd Tanous res.isAliveHelper = nullptr; 5913548d85SEd Tanous } 6013548d85SEd Tanous 61ecd6a3a2SEd Tanous ~Response() = default; 62ecd6a3a2SEd Tanous 63ecd6a3a2SEd Tanous Response(const Response&) = delete; 6472374eb7SNan Zhou 6504e438cbSEd Tanous Response& operator=(const Response& r) = delete; 6604e438cbSEd Tanous 6704e438cbSEd Tanous Response& operator=(Response&& r) noexcept 6804e438cbSEd Tanous { 69*62598e31SEd Tanous BMCWEB_LOG_DEBUG("Moving response containers; this: {}; other: {}", 70*62598e31SEd Tanous logPtr(this), logPtr(&r)); 7172374eb7SNan Zhou if (this == &r) 7272374eb7SNan Zhou { 7372374eb7SNan Zhou return *this; 7472374eb7SNan Zhou } 7504e438cbSEd Tanous stringResponse = std::move(r.stringResponse); 7604e438cbSEd Tanous r.stringResponse.emplace(response_type{}); 7704e438cbSEd Tanous jsonValue = std::move(r.jsonValue); 7813548d85SEd Tanous 7913548d85SEd Tanous // Only need to move completion handler if not already completed 8013548d85SEd Tanous // Note, there are cases where we might move out of a Response object 8113548d85SEd Tanous // while in a completion handler for that response object. This check 8213548d85SEd Tanous // is intended to prevent destructing the functor we are currently 8313548d85SEd Tanous // executing from in that case. 8413548d85SEd Tanous if (!r.completed) 8513548d85SEd Tanous { 8672374eb7SNan Zhou completeRequestHandler = std::move(r.completeRequestHandler); 8772374eb7SNan Zhou r.completeRequestHandler = nullptr; 8813548d85SEd Tanous } 8913548d85SEd Tanous else 9013548d85SEd Tanous { 9113548d85SEd Tanous completeRequestHandler = nullptr; 9213548d85SEd Tanous } 9313548d85SEd Tanous completed = r.completed; 9413548d85SEd Tanous isAliveHelper = std::move(r.isAliveHelper); 9572374eb7SNan Zhou r.isAliveHelper = nullptr; 9604e438cbSEd Tanous return *this; 9704e438cbSEd Tanous } 9804e438cbSEd Tanous 993590bd1dSNan Zhou void result(unsigned v) 1003590bd1dSNan Zhou { 1013590bd1dSNan Zhou stringResponse->result(v); 1023590bd1dSNan Zhou } 1033590bd1dSNan Zhou 10404e438cbSEd Tanous void result(boost::beast::http::status v) 10504e438cbSEd Tanous { 10604e438cbSEd Tanous stringResponse->result(v); 10704e438cbSEd Tanous } 10804e438cbSEd Tanous 109bb60f4deSEd Tanous boost::beast::http::status result() const 11004e438cbSEd Tanous { 11104e438cbSEd Tanous return stringResponse->result(); 11204e438cbSEd Tanous } 11304e438cbSEd Tanous 114039a47e3SCarson Labrado unsigned resultInt() const 11504e438cbSEd Tanous { 11604e438cbSEd Tanous return stringResponse->result_int(); 11704e438cbSEd Tanous } 11804e438cbSEd Tanous 119bb60f4deSEd Tanous std::string_view reason() const 12004e438cbSEd Tanous { 12104e438cbSEd Tanous return stringResponse->reason(); 12204e438cbSEd Tanous } 12304e438cbSEd Tanous 12404e438cbSEd Tanous bool isCompleted() const noexcept 12504e438cbSEd Tanous { 12604e438cbSEd Tanous return completed; 12704e438cbSEd Tanous } 12804e438cbSEd Tanous 12904e438cbSEd Tanous std::string& body() 13004e438cbSEd Tanous { 13104e438cbSEd Tanous return stringResponse->body(); 13204e438cbSEd Tanous } 13304e438cbSEd Tanous 13446a81465SCarson Labrado std::string_view getHeaderValue(std::string_view key) const 13546a81465SCarson Labrado { 13646a81465SCarson Labrado return stringResponse->base()[key]; 13746a81465SCarson Labrado } 13846a81465SCarson Labrado 13904e438cbSEd Tanous void keepAlive(bool k) 14004e438cbSEd Tanous { 14104e438cbSEd Tanous stringResponse->keep_alive(k); 14204e438cbSEd Tanous } 14304e438cbSEd Tanous 144bb60f4deSEd Tanous bool keepAlive() const 14504e438cbSEd Tanous { 14604e438cbSEd Tanous return stringResponse->keep_alive(); 14704e438cbSEd Tanous } 14804e438cbSEd Tanous 14904e438cbSEd Tanous void preparePayload() 15004e438cbSEd Tanous { 151eea9c979SEd Tanous // This code is a throw-free equivalent to 152eea9c979SEd Tanous // beast::http::message::prepare_payload 153eea9c979SEd Tanous boost::optional<uint64_t> pSize = stringResponse->payload_size(); 154eea9c979SEd Tanous using boost::beast::http::status; 155eea9c979SEd Tanous using boost::beast::http::status_class; 156eea9c979SEd Tanous using boost::beast::http::to_status_class; 157eea9c979SEd Tanous if (!pSize) 158eea9c979SEd Tanous { 159eea9c979SEd Tanous pSize = 0; 160eea9c979SEd Tanous } 161eea9c979SEd Tanous else 162eea9c979SEd Tanous { 163eea9c979SEd Tanous bool is1XXReturn = to_status_class(stringResponse->result()) == 164eea9c979SEd Tanous status_class::informational; 165eea9c979SEd Tanous if (*pSize > 0 && 166eea9c979SEd Tanous (is1XXReturn || 167eea9c979SEd Tanous stringResponse->result() == status::no_content || 168eea9c979SEd Tanous stringResponse->result() == status::not_modified)) 169eea9c979SEd Tanous { 170*62598e31SEd Tanous BMCWEB_LOG_CRITICAL( 171*62598e31SEd Tanous "{} Response content provided but code was no-content or not_modified, which aren't allowed to have a body", 172*62598e31SEd Tanous logPtr(this)); 173eea9c979SEd Tanous pSize = 0; 174eea9c979SEd Tanous body().clear(); 175eea9c979SEd Tanous } 176eea9c979SEd Tanous } 177eea9c979SEd Tanous stringResponse->content_length(*pSize); 17804e438cbSEd Tanous } 17904e438cbSEd Tanous 18004e438cbSEd Tanous void clear() 18104e438cbSEd Tanous { 182*62598e31SEd Tanous BMCWEB_LOG_DEBUG("{} Clearing response containers", logPtr(this)); 18304e438cbSEd Tanous stringResponse.emplace(response_type{}); 184a6695a84SEd Tanous jsonValue = nullptr; 18504e438cbSEd Tanous completed = false; 186291d709dSEd Tanous expectedHash = std::nullopt; 18704e438cbSEd Tanous } 18804e438cbSEd Tanous 18981ce609eSEd Tanous void write(std::string_view bodyPart) 19004e438cbSEd Tanous { 19181ce609eSEd Tanous stringResponse->body() += std::string(bodyPart); 19204e438cbSEd Tanous } 19304e438cbSEd Tanous 1942d6cb56bSEd Tanous std::string computeEtag() const 19504e438cbSEd Tanous { 19689f18008SEd Tanous // Only set etag if this request succeeded 1972d6cb56bSEd Tanous if (result() != boost::beast::http::status::ok) 19889f18008SEd Tanous { 1992d6cb56bSEd Tanous return ""; 20089f18008SEd Tanous } 2012d6cb56bSEd Tanous // and the json response isn't empty 2022d6cb56bSEd Tanous if (jsonValue.empty()) 2032d6cb56bSEd Tanous { 2042d6cb56bSEd Tanous return ""; 2052d6cb56bSEd Tanous } 2062d6cb56bSEd Tanous size_t hashval = std::hash<nlohmann::json>{}(jsonValue); 2072d6cb56bSEd Tanous return "\"" + intToHexString(hashval, 8) + "\""; 2082d6cb56bSEd Tanous } 2092d6cb56bSEd Tanous 2102d6cb56bSEd Tanous void end() 2112d6cb56bSEd Tanous { 2122d6cb56bSEd Tanous std::string etag = computeEtag(); 2132d6cb56bSEd Tanous if (!etag.empty()) 2142d6cb56bSEd Tanous { 2152d6cb56bSEd Tanous addHeader(boost::beast::http::field::etag, etag); 21689f18008SEd Tanous } 21704e438cbSEd Tanous if (completed) 21804e438cbSEd Tanous { 219*62598e31SEd Tanous BMCWEB_LOG_ERROR("{} Response was ended twice", logPtr(this)); 22004e438cbSEd Tanous return; 22104e438cbSEd Tanous } 22204e438cbSEd Tanous completed = true; 223*62598e31SEd Tanous BMCWEB_LOG_DEBUG("{} calling completion handler", logPtr(this)); 22404e438cbSEd Tanous if (completeRequestHandler) 22504e438cbSEd Tanous { 226*62598e31SEd Tanous BMCWEB_LOG_DEBUG("{} completion handler was valid", logPtr(this)); 22772374eb7SNan Zhou completeRequestHandler(*this); 22804e438cbSEd Tanous } 22904e438cbSEd Tanous } 23004e438cbSEd Tanous 231bb60f4deSEd Tanous bool isAlive() const 23204e438cbSEd Tanous { 23304e438cbSEd Tanous return isAliveHelper && isAliveHelper(); 23404e438cbSEd Tanous } 23504e438cbSEd Tanous 23672374eb7SNan Zhou void setCompleteRequestHandler(std::function<void(Response&)>&& handler) 2374147b8acSJohn Edward Broadbent { 238*62598e31SEd Tanous BMCWEB_LOG_DEBUG("{} setting completion handler", logPtr(this)); 23972374eb7SNan Zhou completeRequestHandler = std::move(handler); 24013548d85SEd Tanous 24113548d85SEd Tanous // Now that we have a new completion handler attached, we're no longer 24213548d85SEd Tanous // complete 24313548d85SEd Tanous completed = false; 24472374eb7SNan Zhou } 24572374eb7SNan Zhou 24672374eb7SNan Zhou std::function<void(Response&)> releaseCompleteRequestHandler() 24772374eb7SNan Zhou { 248*62598e31SEd Tanous BMCWEB_LOG_DEBUG("{} releasing completion handler{}", logPtr(this), 249*62598e31SEd Tanous static_cast<bool>(completeRequestHandler)); 25072374eb7SNan Zhou std::function<void(Response&)> ret = completeRequestHandler; 25172374eb7SNan Zhou completeRequestHandler = nullptr; 25213548d85SEd Tanous completed = true; 25372374eb7SNan Zhou return ret; 25472374eb7SNan Zhou } 25572374eb7SNan Zhou 25672374eb7SNan Zhou void setIsAliveHelper(std::function<bool()>&& handler) 25772374eb7SNan Zhou { 25872374eb7SNan Zhou isAliveHelper = std::move(handler); 25972374eb7SNan Zhou } 26072374eb7SNan Zhou 26172374eb7SNan Zhou std::function<bool()> releaseIsAliveHelper() 26272374eb7SNan Zhou { 26372374eb7SNan Zhou std::function<bool()> ret = std::move(isAliveHelper); 26472374eb7SNan Zhou isAliveHelper = nullptr; 26572374eb7SNan Zhou return ret; 2664147b8acSJohn Edward Broadbent } 2674147b8acSJohn Edward Broadbent 268291d709dSEd Tanous void setHashAndHandleNotModified() 269291d709dSEd Tanous { 270291d709dSEd Tanous // Can only hash if we have content that's valid 271291d709dSEd Tanous if (jsonValue.empty() || result() != boost::beast::http::status::ok) 272291d709dSEd Tanous { 273291d709dSEd Tanous return; 274291d709dSEd Tanous } 275291d709dSEd Tanous size_t hashval = std::hash<nlohmann::json>{}(jsonValue); 276291d709dSEd Tanous std::string hexVal = "\"" + intToHexString(hashval, 8) + "\""; 277291d709dSEd Tanous addHeader(boost::beast::http::field::etag, hexVal); 278291d709dSEd Tanous if (expectedHash && hexVal == *expectedHash) 279291d709dSEd Tanous { 280a6695a84SEd Tanous jsonValue = nullptr; 281291d709dSEd Tanous result(boost::beast::http::status::not_modified); 282291d709dSEd Tanous } 283291d709dSEd Tanous } 284291d709dSEd Tanous 285291d709dSEd Tanous void setExpectedHash(std::string_view hash) 286291d709dSEd Tanous { 287291d709dSEd Tanous expectedHash = hash; 288291d709dSEd Tanous } 289291d709dSEd Tanous 29004e438cbSEd Tanous private: 291291d709dSEd Tanous std::optional<std::string> expectedHash; 29272374eb7SNan Zhou bool completed = false; 29372374eb7SNan Zhou std::function<void(Response&)> completeRequestHandler; 29404e438cbSEd Tanous std::function<bool()> isAliveHelper; 29504e438cbSEd Tanous }; 29604e438cbSEd Tanous } // namespace crow 297