104e438cbSEd Tanous #pragma once 204e438cbSEd Tanous #include "logging.hpp" 304e438cbSEd Tanous #include "nlohmann/json.hpp" 404e438cbSEd Tanous 504e438cbSEd Tanous #include <boost/beast/http/message.hpp> 6d4b6c660SEd Tanous #include <boost/beast/http/string_body.hpp> 789f18008SEd Tanous #include <utils/hex_utils.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 3004e438cbSEd Tanous void addHeader(const std::string_view key, const std::string_view value) 3104e438cbSEd Tanous { 3204e438cbSEd Tanous stringResponse->set(key, value); 3304e438cbSEd Tanous } 3404e438cbSEd Tanous 3504e438cbSEd Tanous void addHeader(boost::beast::http::field key, std::string_view value) 3604e438cbSEd Tanous { 3704e438cbSEd Tanous stringResponse->set(key, value); 3804e438cbSEd Tanous } 3904e438cbSEd Tanous 4004e438cbSEd Tanous Response() : stringResponse(response_type{}) 4104e438cbSEd Tanous {} 4204e438cbSEd Tanous 4313548d85SEd Tanous Response(Response&& res) noexcept : 4413548d85SEd Tanous stringResponse(std::move(res.stringResponse)), completed(res.completed) 4513548d85SEd Tanous { 4613548d85SEd Tanous jsonValue = std::move(res.jsonValue); 4713548d85SEd Tanous // See note in operator= move handler for why this is needed. 4813548d85SEd Tanous if (!res.completed) 4913548d85SEd Tanous { 5013548d85SEd Tanous completeRequestHandler = std::move(res.completeRequestHandler); 5113548d85SEd Tanous res.completeRequestHandler = nullptr; 5213548d85SEd Tanous } 5313548d85SEd Tanous isAliveHelper = res.isAliveHelper; 5413548d85SEd Tanous res.isAliveHelper = nullptr; 5513548d85SEd Tanous } 5613548d85SEd Tanous 57ecd6a3a2SEd Tanous ~Response() = default; 58ecd6a3a2SEd Tanous 59ecd6a3a2SEd Tanous Response(const Response&) = delete; 6072374eb7SNan Zhou 6104e438cbSEd Tanous Response& operator=(const Response& r) = delete; 6204e438cbSEd Tanous 6304e438cbSEd Tanous Response& operator=(Response&& r) noexcept 6404e438cbSEd Tanous { 6572374eb7SNan Zhou BMCWEB_LOG_DEBUG << "Moving response containers; this: " << this 6672374eb7SNan Zhou << "; other: " << &r; 6772374eb7SNan Zhou if (this == &r) 6872374eb7SNan Zhou { 6972374eb7SNan Zhou return *this; 7072374eb7SNan Zhou } 7104e438cbSEd Tanous stringResponse = std::move(r.stringResponse); 7204e438cbSEd Tanous r.stringResponse.emplace(response_type{}); 7304e438cbSEd Tanous jsonValue = std::move(r.jsonValue); 7413548d85SEd Tanous 7513548d85SEd Tanous // Only need to move completion handler if not already completed 7613548d85SEd Tanous // Note, there are cases where we might move out of a Response object 7713548d85SEd Tanous // while in a completion handler for that response object. This check 7813548d85SEd Tanous // is intended to prevent destructing the functor we are currently 7913548d85SEd Tanous // executing from in that case. 8013548d85SEd Tanous if (!r.completed) 8113548d85SEd Tanous { 8272374eb7SNan Zhou completeRequestHandler = std::move(r.completeRequestHandler); 8372374eb7SNan Zhou r.completeRequestHandler = nullptr; 8413548d85SEd Tanous } 8513548d85SEd Tanous else 8613548d85SEd Tanous { 8713548d85SEd Tanous completeRequestHandler = nullptr; 8813548d85SEd Tanous } 8913548d85SEd Tanous completed = r.completed; 9013548d85SEd Tanous isAliveHelper = std::move(r.isAliveHelper); 9172374eb7SNan Zhou r.isAliveHelper = nullptr; 9204e438cbSEd Tanous return *this; 9304e438cbSEd Tanous } 9404e438cbSEd Tanous 953590bd1dSNan Zhou void result(unsigned v) 963590bd1dSNan Zhou { 973590bd1dSNan Zhou stringResponse->result(v); 983590bd1dSNan Zhou } 993590bd1dSNan Zhou 10004e438cbSEd Tanous void result(boost::beast::http::status v) 10104e438cbSEd Tanous { 10204e438cbSEd Tanous stringResponse->result(v); 10304e438cbSEd Tanous } 10404e438cbSEd Tanous 105bb60f4deSEd Tanous boost::beast::http::status result() const 10604e438cbSEd Tanous { 10704e438cbSEd Tanous return stringResponse->result(); 10804e438cbSEd Tanous } 10904e438cbSEd Tanous 110039a47e3SCarson Labrado unsigned resultInt() const 11104e438cbSEd Tanous { 11204e438cbSEd Tanous return stringResponse->result_int(); 11304e438cbSEd Tanous } 11404e438cbSEd Tanous 115bb60f4deSEd Tanous std::string_view reason() const 11604e438cbSEd Tanous { 11704e438cbSEd Tanous return stringResponse->reason(); 11804e438cbSEd Tanous } 11904e438cbSEd Tanous 12004e438cbSEd Tanous bool isCompleted() const noexcept 12104e438cbSEd Tanous { 12204e438cbSEd Tanous return completed; 12304e438cbSEd Tanous } 12404e438cbSEd Tanous 12504e438cbSEd Tanous std::string& body() 12604e438cbSEd Tanous { 12704e438cbSEd Tanous return stringResponse->body(); 12804e438cbSEd Tanous } 12904e438cbSEd Tanous 13046a81465SCarson Labrado std::string_view getHeaderValue(std::string_view key) const 13146a81465SCarson Labrado { 13246a81465SCarson Labrado return stringResponse->base()[key]; 13346a81465SCarson Labrado } 13446a81465SCarson Labrado 13504e438cbSEd Tanous void keepAlive(bool k) 13604e438cbSEd Tanous { 13704e438cbSEd Tanous stringResponse->keep_alive(k); 13804e438cbSEd Tanous } 13904e438cbSEd Tanous 140bb60f4deSEd Tanous bool keepAlive() const 14104e438cbSEd Tanous { 14204e438cbSEd Tanous return stringResponse->keep_alive(); 14304e438cbSEd Tanous } 14404e438cbSEd Tanous 14504e438cbSEd Tanous void preparePayload() 14604e438cbSEd Tanous { 14704e438cbSEd Tanous stringResponse->prepare_payload(); 14804e438cbSEd Tanous } 14904e438cbSEd Tanous 15004e438cbSEd Tanous void clear() 15104e438cbSEd Tanous { 15204e438cbSEd Tanous BMCWEB_LOG_DEBUG << this << " Clearing response containers"; 15304e438cbSEd Tanous stringResponse.emplace(response_type{}); 15404e438cbSEd Tanous jsonValue.clear(); 15504e438cbSEd Tanous completed = false; 156*291d709dSEd Tanous expectedHash = std::nullopt; 15704e438cbSEd Tanous } 15804e438cbSEd Tanous 15981ce609eSEd Tanous void write(std::string_view bodyPart) 16004e438cbSEd Tanous { 16181ce609eSEd Tanous stringResponse->body() += std::string(bodyPart); 16204e438cbSEd Tanous } 16304e438cbSEd Tanous 16404e438cbSEd Tanous void end() 16504e438cbSEd Tanous { 16689f18008SEd Tanous // Only set etag if this request succeeded 16789f18008SEd Tanous if (result() == boost::beast::http::status::ok) 16889f18008SEd Tanous { 16989f18008SEd Tanous // and the json response isn't empty 17089f18008SEd Tanous if (!jsonValue.empty()) 17189f18008SEd Tanous { 17289f18008SEd Tanous size_t hashval = std::hash<nlohmann::json>{}(jsonValue); 17389f18008SEd Tanous std::string hexVal = "\"" + intToHexString(hashval, 8) + "\""; 17489f18008SEd Tanous addHeader(boost::beast::http::field::etag, hexVal); 17589f18008SEd Tanous } 17689f18008SEd Tanous } 17704e438cbSEd Tanous if (completed) 17804e438cbSEd Tanous { 17972374eb7SNan Zhou BMCWEB_LOG_ERROR << this << " Response was ended twice"; 18004e438cbSEd Tanous return; 18104e438cbSEd Tanous } 18204e438cbSEd Tanous completed = true; 18372374eb7SNan Zhou BMCWEB_LOG_DEBUG << this << " calling completion handler"; 18404e438cbSEd Tanous if (completeRequestHandler) 18504e438cbSEd Tanous { 18672374eb7SNan Zhou BMCWEB_LOG_DEBUG << this << " completion handler was valid"; 18772374eb7SNan Zhou completeRequestHandler(*this); 18804e438cbSEd Tanous } 18904e438cbSEd Tanous } 19004e438cbSEd Tanous 191bb60f4deSEd Tanous bool isAlive() const 19204e438cbSEd Tanous { 19304e438cbSEd Tanous return isAliveHelper && isAliveHelper(); 19404e438cbSEd Tanous } 19504e438cbSEd Tanous 19672374eb7SNan Zhou void setCompleteRequestHandler(std::function<void(Response&)>&& handler) 1974147b8acSJohn Edward Broadbent { 19872374eb7SNan Zhou BMCWEB_LOG_DEBUG << this << " setting completion handler"; 19972374eb7SNan Zhou completeRequestHandler = std::move(handler); 20013548d85SEd Tanous 20113548d85SEd Tanous // Now that we have a new completion handler attached, we're no longer 20213548d85SEd Tanous // complete 20313548d85SEd Tanous completed = false; 20472374eb7SNan Zhou } 20572374eb7SNan Zhou 20672374eb7SNan Zhou std::function<void(Response&)> releaseCompleteRequestHandler() 20772374eb7SNan Zhou { 20872374eb7SNan Zhou BMCWEB_LOG_DEBUG << this << " releasing completion handler" 20972374eb7SNan Zhou << static_cast<bool>(completeRequestHandler); 21072374eb7SNan Zhou std::function<void(Response&)> ret = completeRequestHandler; 21172374eb7SNan Zhou completeRequestHandler = nullptr; 21213548d85SEd Tanous completed = true; 21372374eb7SNan Zhou return ret; 21472374eb7SNan Zhou } 21572374eb7SNan Zhou 21672374eb7SNan Zhou void setIsAliveHelper(std::function<bool()>&& handler) 21772374eb7SNan Zhou { 21872374eb7SNan Zhou isAliveHelper = std::move(handler); 21972374eb7SNan Zhou } 22072374eb7SNan Zhou 22172374eb7SNan Zhou std::function<bool()> releaseIsAliveHelper() 22272374eb7SNan Zhou { 22372374eb7SNan Zhou std::function<bool()> ret = std::move(isAliveHelper); 22472374eb7SNan Zhou isAliveHelper = nullptr; 22572374eb7SNan Zhou return ret; 2264147b8acSJohn Edward Broadbent } 2274147b8acSJohn Edward Broadbent 228*291d709dSEd Tanous void setHashAndHandleNotModified() 229*291d709dSEd Tanous { 230*291d709dSEd Tanous // Can only hash if we have content that's valid 231*291d709dSEd Tanous if (jsonValue.empty() || result() != boost::beast::http::status::ok) 232*291d709dSEd Tanous { 233*291d709dSEd Tanous return; 234*291d709dSEd Tanous } 235*291d709dSEd Tanous size_t hashval = std::hash<nlohmann::json>{}(jsonValue); 236*291d709dSEd Tanous std::string hexVal = "\"" + intToHexString(hashval, 8) + "\""; 237*291d709dSEd Tanous addHeader(boost::beast::http::field::etag, hexVal); 238*291d709dSEd Tanous if (expectedHash && hexVal == *expectedHash) 239*291d709dSEd Tanous { 240*291d709dSEd Tanous jsonValue.clear(); 241*291d709dSEd Tanous result(boost::beast::http::status::not_modified); 242*291d709dSEd Tanous } 243*291d709dSEd Tanous } 244*291d709dSEd Tanous 245*291d709dSEd Tanous void setExpectedHash(std::string_view hash) 246*291d709dSEd Tanous { 247*291d709dSEd Tanous expectedHash = hash; 248*291d709dSEd Tanous } 249*291d709dSEd Tanous 25004e438cbSEd Tanous private: 251*291d709dSEd Tanous std::optional<std::string> expectedHash; 25272374eb7SNan Zhou bool completed = false; 25372374eb7SNan Zhou std::function<void(Response&)> completeRequestHandler; 25404e438cbSEd Tanous std::function<bool()> isAliveHelper; 25504e438cbSEd Tanous }; 25604e438cbSEd Tanous } // namespace crow 257