104e438cbSEd Tanous #pragma once 2b2896149SEd Tanous #include "http_body.hpp" 304e438cbSEd Tanous #include "logging.hpp" 43ccb3adbSEd Tanous #include "utils/hex_utils.hpp" 504e438cbSEd Tanous 604e438cbSEd Tanous #include <boost/beast/http/message.hpp> 7faf100f9SEd Tanous #include <nlohmann/json.hpp> 804e438cbSEd Tanous 98a9a25c8SEd Tanous #include <optional> 1004e438cbSEd Tanous #include <string> 118a9a25c8SEd Tanous #include <string_view> 128e3f7032SAbhilash Raju #include <utility> 1304e438cbSEd Tanous namespace crow 1404e438cbSEd Tanous { 1504e438cbSEd Tanous 1604e438cbSEd Tanous template <typename Adaptor, typename Handler> 1704e438cbSEd Tanous class Connection; 1804e438cbSEd Tanous 1927b0cf90SEd Tanous namespace http = boost::beast::http; 2027b0cf90SEd Tanous 2104e438cbSEd Tanous struct Response 2204e438cbSEd Tanous { 2304e438cbSEd Tanous template <typename Adaptor, typename Handler> 2404e438cbSEd Tanous friend class crow::Connection; 2504e438cbSEd Tanous 26b2896149SEd Tanous http::response<bmcweb::HttpBody> response; 2704e438cbSEd Tanous 2804e438cbSEd Tanous nlohmann::json jsonValue; 2927b0cf90SEd Tanous using fields_type = http::header<false, http::fields>; 3027b0cf90SEd Tanous fields_type& fields() 3127b0cf90SEd Tanous { 3252e31629SEd Tanous return response.base(); 3327b0cf90SEd Tanous } 3427b0cf90SEd Tanous 3527b0cf90SEd Tanous const fields_type& fields() const 3627b0cf90SEd Tanous { 3752e31629SEd Tanous return response.base(); 3827b0cf90SEd Tanous } 3904e438cbSEd Tanous 4026ccae32SEd Tanous void addHeader(std::string_view key, std::string_view value) 4104e438cbSEd Tanous { 4227b0cf90SEd Tanous fields().insert(key, value); 4304e438cbSEd Tanous } 4404e438cbSEd Tanous 4527b0cf90SEd Tanous void addHeader(http::field key, std::string_view value) 4604e438cbSEd Tanous { 4727b0cf90SEd Tanous fields().insert(key, value); 48994fd86aSEd Tanous } 49994fd86aSEd Tanous 5027b0cf90SEd Tanous void clearHeader(http::field key) 51994fd86aSEd Tanous { 5227b0cf90SEd Tanous fields().erase(key); 5304e438cbSEd Tanous } 5404e438cbSEd Tanous 5552e31629SEd Tanous Response() = default; 5613548d85SEd Tanous Response(Response&& res) noexcept : 5727b0cf90SEd Tanous response(std::move(res.response)), jsonValue(std::move(res.jsonValue)), 5827b0cf90SEd Tanous completed(res.completed) 5913548d85SEd Tanous { 6013548d85SEd Tanous // See note in operator= move handler for why this is needed. 6113548d85SEd Tanous if (!res.completed) 6213548d85SEd Tanous { 6313548d85SEd Tanous completeRequestHandler = std::move(res.completeRequestHandler); 6413548d85SEd Tanous res.completeRequestHandler = nullptr; 6513548d85SEd Tanous } 6613548d85SEd Tanous } 6713548d85SEd Tanous 68ecd6a3a2SEd Tanous ~Response() = default; 69ecd6a3a2SEd Tanous 70ecd6a3a2SEd Tanous Response(const Response&) = delete; 7104e438cbSEd Tanous Response& operator=(const Response& r) = delete; 7204e438cbSEd Tanous 7304e438cbSEd Tanous Response& operator=(Response&& r) noexcept 7404e438cbSEd Tanous { 7562598e31SEd Tanous BMCWEB_LOG_DEBUG("Moving response containers; this: {}; other: {}", 7662598e31SEd Tanous logPtr(this), logPtr(&r)); 7772374eb7SNan Zhou if (this == &r) 7872374eb7SNan Zhou { 7972374eb7SNan Zhou return *this; 8072374eb7SNan Zhou } 8127b0cf90SEd Tanous response = std::move(r.response); 8204e438cbSEd Tanous jsonValue = std::move(r.jsonValue); 8313548d85SEd Tanous 8413548d85SEd Tanous // Only need to move completion handler if not already completed 8513548d85SEd Tanous // Note, there are cases where we might move out of a Response object 8613548d85SEd Tanous // while in a completion handler for that response object. This check 8713548d85SEd Tanous // is intended to prevent destructing the functor we are currently 8813548d85SEd Tanous // executing from in that case. 8913548d85SEd Tanous if (!r.completed) 9013548d85SEd Tanous { 9172374eb7SNan Zhou completeRequestHandler = std::move(r.completeRequestHandler); 9272374eb7SNan Zhou r.completeRequestHandler = nullptr; 9313548d85SEd Tanous } 9413548d85SEd Tanous else 9513548d85SEd Tanous { 9613548d85SEd Tanous completeRequestHandler = nullptr; 9713548d85SEd Tanous } 9813548d85SEd Tanous completed = r.completed; 9904e438cbSEd Tanous return *this; 10004e438cbSEd Tanous } 10104e438cbSEd Tanous 1023590bd1dSNan Zhou void result(unsigned v) 1033590bd1dSNan Zhou { 10427b0cf90SEd Tanous fields().result(v); 1053590bd1dSNan Zhou } 1063590bd1dSNan Zhou 10727b0cf90SEd Tanous void result(http::status v) 10804e438cbSEd Tanous { 10927b0cf90SEd Tanous fields().result(v); 11004e438cbSEd Tanous } 11104e438cbSEd Tanous 11227b0cf90SEd Tanous void copyBody(const Response& res) 11304e438cbSEd Tanous { 11452e31629SEd Tanous response.body() = res.response.body(); 11527b0cf90SEd Tanous } 11627b0cf90SEd Tanous 11727b0cf90SEd Tanous http::status result() const 11827b0cf90SEd Tanous { 11927b0cf90SEd Tanous return fields().result(); 12004e438cbSEd Tanous } 12104e438cbSEd Tanous 122039a47e3SCarson Labrado unsigned resultInt() const 12304e438cbSEd Tanous { 12427b0cf90SEd Tanous return fields().result_int(); 12504e438cbSEd Tanous } 12604e438cbSEd Tanous 127bb60f4deSEd Tanous std::string_view reason() const 12804e438cbSEd Tanous { 12927b0cf90SEd Tanous return fields().reason(); 13004e438cbSEd Tanous } 13104e438cbSEd Tanous 13204e438cbSEd Tanous bool isCompleted() const noexcept 13304e438cbSEd Tanous { 13404e438cbSEd Tanous return completed; 13504e438cbSEd Tanous } 13604e438cbSEd Tanous 13727b0cf90SEd Tanous const std::string* body() 13804e438cbSEd Tanous { 13952e31629SEd Tanous return &response.body().str(); 14004e438cbSEd Tanous } 14104e438cbSEd Tanous 14246a81465SCarson Labrado std::string_view getHeaderValue(std::string_view key) const 14346a81465SCarson Labrado { 14427b0cf90SEd Tanous return fields()[key]; 14546a81465SCarson Labrado } 14646a81465SCarson Labrado 14704e438cbSEd Tanous void keepAlive(bool k) 14804e438cbSEd Tanous { 14952e31629SEd Tanous response.keep_alive(k); 15004e438cbSEd Tanous } 15104e438cbSEd Tanous 152bb60f4deSEd Tanous bool keepAlive() const 15304e438cbSEd Tanous { 15452e31629SEd Tanous return response.keep_alive(); 15527b0cf90SEd Tanous } 15627b0cf90SEd Tanous 15752e31629SEd Tanous std::optional<uint64_t> size() 15852e31629SEd Tanous { 15952e31629SEd Tanous return response.body().payloadSize(); 16052e31629SEd Tanous } 16152e31629SEd Tanous 16252e31629SEd Tanous void preparePayload() 16327b0cf90SEd Tanous { 16427b0cf90SEd Tanous // This code is a throw-free equivalent to 16527b0cf90SEd Tanous // beast::http::message::prepare_payload 16652e31629SEd Tanous std::optional<uint64_t> pSize = response.body().payloadSize(); 16752e31629SEd Tanous if (!pSize) 16852e31629SEd Tanous { 16952e31629SEd Tanous return; 17052e31629SEd Tanous } 17127b0cf90SEd Tanous using http::status; 17227b0cf90SEd Tanous using http::status_class; 17327b0cf90SEd Tanous using http::to_status_class; 17427b0cf90SEd Tanous bool is1XXReturn = to_status_class(result()) == 17527b0cf90SEd Tanous status_class::informational; 17627b0cf90SEd Tanous if (*pSize > 0 && (is1XXReturn || result() == status::no_content || 17727b0cf90SEd Tanous result() == status::not_modified)) 17827b0cf90SEd Tanous { 17927b0cf90SEd Tanous BMCWEB_LOG_CRITICAL("{} Response content provided but code was " 18027b0cf90SEd Tanous "no-content or not_modified, which aren't " 18127b0cf90SEd Tanous "allowed to have a body", 18227b0cf90SEd Tanous logPtr(this)); 18352e31629SEd Tanous response.content_length(0); 18452e31629SEd Tanous return; 18527b0cf90SEd Tanous } 18652e31629SEd Tanous response.content_length(*pSize); 18704e438cbSEd Tanous } 18804e438cbSEd Tanous 18904e438cbSEd Tanous void clear() 19004e438cbSEd Tanous { 19162598e31SEd Tanous BMCWEB_LOG_DEBUG("{} Clearing response containers", logPtr(this)); 19252e31629SEd Tanous response.clear(); 193*06fc9bebSEd Tanous response.body().clear(); 19452e31629SEd Tanous 195a6695a84SEd Tanous jsonValue = nullptr; 19604e438cbSEd Tanous completed = false; 197291d709dSEd Tanous expectedHash = std::nullopt; 19804e438cbSEd Tanous } 19904e438cbSEd Tanous 2002d6cb56bSEd Tanous std::string computeEtag() const 20104e438cbSEd Tanous { 20289f18008SEd Tanous // Only set etag if this request succeeded 20327b0cf90SEd Tanous if (result() != http::status::ok) 20489f18008SEd Tanous { 2052d6cb56bSEd Tanous return ""; 20689f18008SEd Tanous } 2072d6cb56bSEd Tanous // and the json response isn't empty 2082d6cb56bSEd Tanous if (jsonValue.empty()) 2092d6cb56bSEd Tanous { 2102d6cb56bSEd Tanous return ""; 2112d6cb56bSEd Tanous } 2122d6cb56bSEd Tanous size_t hashval = std::hash<nlohmann::json>{}(jsonValue); 2132d6cb56bSEd Tanous return "\"" + intToHexString(hashval, 8) + "\""; 2142d6cb56bSEd Tanous } 2152d6cb56bSEd Tanous 21627b0cf90SEd Tanous void write(std::string&& bodyPart) 21727b0cf90SEd Tanous { 21852e31629SEd Tanous response.body().str() = std::move(bodyPart); 21927b0cf90SEd Tanous } 22027b0cf90SEd Tanous 2212d6cb56bSEd Tanous void end() 2222d6cb56bSEd Tanous { 22304e438cbSEd Tanous if (completed) 22404e438cbSEd Tanous { 22562598e31SEd Tanous BMCWEB_LOG_ERROR("{} Response was ended twice", logPtr(this)); 22604e438cbSEd Tanous return; 22704e438cbSEd Tanous } 22804e438cbSEd Tanous completed = true; 22962598e31SEd Tanous BMCWEB_LOG_DEBUG("{} calling completion handler", logPtr(this)); 23004e438cbSEd Tanous if (completeRequestHandler) 23104e438cbSEd Tanous { 23262598e31SEd Tanous BMCWEB_LOG_DEBUG("{} completion handler was valid", logPtr(this)); 23372374eb7SNan Zhou completeRequestHandler(*this); 23404e438cbSEd Tanous } 23504e438cbSEd Tanous } 23604e438cbSEd Tanous 23772374eb7SNan Zhou void setCompleteRequestHandler(std::function<void(Response&)>&& handler) 2384147b8acSJohn Edward Broadbent { 23962598e31SEd Tanous BMCWEB_LOG_DEBUG("{} setting completion handler", logPtr(this)); 24072374eb7SNan Zhou completeRequestHandler = std::move(handler); 24113548d85SEd Tanous 24213548d85SEd Tanous // Now that we have a new completion handler attached, we're no longer 24313548d85SEd Tanous // complete 24413548d85SEd Tanous completed = false; 24572374eb7SNan Zhou } 24672374eb7SNan Zhou 24772374eb7SNan Zhou std::function<void(Response&)> releaseCompleteRequestHandler() 24872374eb7SNan Zhou { 24962598e31SEd Tanous BMCWEB_LOG_DEBUG("{} releasing completion handler{}", logPtr(this), 25062598e31SEd Tanous static_cast<bool>(completeRequestHandler)); 25172374eb7SNan Zhou std::function<void(Response&)> ret = completeRequestHandler; 25272374eb7SNan Zhou completeRequestHandler = nullptr; 25313548d85SEd Tanous completed = true; 25472374eb7SNan Zhou return ret; 25572374eb7SNan Zhou } 25672374eb7SNan Zhou 257291d709dSEd Tanous void setHashAndHandleNotModified() 258291d709dSEd Tanous { 259291d709dSEd Tanous // Can only hash if we have content that's valid 26027b0cf90SEd Tanous if (jsonValue.empty() || result() != http::status::ok) 261291d709dSEd Tanous { 262291d709dSEd Tanous return; 263291d709dSEd Tanous } 264291d709dSEd Tanous size_t hashval = std::hash<nlohmann::json>{}(jsonValue); 265291d709dSEd Tanous std::string hexVal = "\"" + intToHexString(hashval, 8) + "\""; 26627b0cf90SEd Tanous addHeader(http::field::etag, hexVal); 267291d709dSEd Tanous if (expectedHash && hexVal == *expectedHash) 268291d709dSEd Tanous { 269a6695a84SEd Tanous jsonValue = nullptr; 27027b0cf90SEd Tanous result(http::status::not_modified); 271291d709dSEd Tanous } 272291d709dSEd Tanous } 273291d709dSEd Tanous 274291d709dSEd Tanous void setExpectedHash(std::string_view hash) 275291d709dSEd Tanous { 276291d709dSEd Tanous expectedHash = hash; 277291d709dSEd Tanous } 278291d709dSEd Tanous 279b5f288d2SAbhilash Raju bool openFile(const std::filesystem::path& path, 280b5f288d2SAbhilash Raju bmcweb::EncodingType enc = bmcweb::EncodingType::Raw) 28127b0cf90SEd Tanous { 28227b0cf90SEd Tanous boost::beast::error_code ec; 28352e31629SEd Tanous response.body().open(path.c_str(), boost::beast::file_mode::read, ec); 28452e31629SEd Tanous response.body().encodingType = enc; 28527b0cf90SEd Tanous if (ec) 28627b0cf90SEd Tanous { 28752e31629SEd Tanous BMCWEB_LOG_ERROR("Failed to open file {}", path.c_str()); 28827b0cf90SEd Tanous return false; 28927b0cf90SEd Tanous } 290b5f288d2SAbhilash Raju return true; 291b5f288d2SAbhilash Raju } 292b5f288d2SAbhilash Raju 293b5f288d2SAbhilash Raju bool openFd(int fd, bmcweb::EncodingType enc = bmcweb::EncodingType::Raw) 294b5f288d2SAbhilash Raju { 295b5f288d2SAbhilash Raju boost::beast::error_code ec; 29652e31629SEd Tanous response.body().encodingType = enc; 29752e31629SEd Tanous response.body().setFd(fd, ec); 298b5f288d2SAbhilash Raju if (ec) 299b5f288d2SAbhilash Raju { 300b5f288d2SAbhilash Raju BMCWEB_LOG_ERROR("Failed to set fd"); 301b5f288d2SAbhilash Raju return false; 302b5f288d2SAbhilash Raju } 303b5f288d2SAbhilash Raju return true; 304b5f288d2SAbhilash Raju } 305b5f288d2SAbhilash Raju 306b5f288d2SAbhilash Raju private: 307291d709dSEd Tanous std::optional<std::string> expectedHash; 30872374eb7SNan Zhou bool completed = false; 30972374eb7SNan Zhou std::function<void(Response&)> completeRequestHandler; 31004e438cbSEd Tanous }; 31104e438cbSEd Tanous } // namespace crow 312