xref: /openbmc/bmcweb/http/http_response.hpp (revision 26ccae32112679c4653c1e3f8a1203c828bea05c)
104e438cbSEd Tanous #pragma once
204e438cbSEd Tanous #include "logging.hpp"
304e438cbSEd Tanous #include "nlohmann/json.hpp"
43ccb3adbSEd Tanous #include "utils/hex_utils.hpp"
504e438cbSEd Tanous 
604e438cbSEd Tanous #include <boost/beast/http/message.hpp>
7d4b6c660SEd Tanous #include <boost/beast/http/string_body.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 
30*26ccae32SEd Tanous     void addHeader(std::string_view key, 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 :
44f8fe53e7SEd Tanous         stringResponse(std::move(res.stringResponse)),
45f8fe53e7SEd Tanous         jsonValue(std::move(res.jsonValue)), completed(res.completed)
4613548d85SEd Tanous     {
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;
156291d709dSEd 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 
1642d6cb56bSEd Tanous     std::string computeEtag() const
16504e438cbSEd Tanous     {
16689f18008SEd Tanous         // Only set etag if this request succeeded
1672d6cb56bSEd Tanous         if (result() != boost::beast::http::status::ok)
16889f18008SEd Tanous         {
1692d6cb56bSEd Tanous             return "";
17089f18008SEd Tanous         }
1712d6cb56bSEd Tanous         // and the json response isn't empty
1722d6cb56bSEd Tanous         if (jsonValue.empty())
1732d6cb56bSEd Tanous         {
1742d6cb56bSEd Tanous             return "";
1752d6cb56bSEd Tanous         }
1762d6cb56bSEd Tanous         size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
1772d6cb56bSEd Tanous         return "\"" + intToHexString(hashval, 8) + "\"";
1782d6cb56bSEd Tanous     }
1792d6cb56bSEd Tanous 
1802d6cb56bSEd Tanous     void end()
1812d6cb56bSEd Tanous     {
1822d6cb56bSEd Tanous         std::string etag = computeEtag();
1832d6cb56bSEd Tanous         if (!etag.empty())
1842d6cb56bSEd Tanous         {
1852d6cb56bSEd Tanous             addHeader(boost::beast::http::field::etag, etag);
18689f18008SEd Tanous         }
18704e438cbSEd Tanous         if (completed)
18804e438cbSEd Tanous         {
18972374eb7SNan Zhou             BMCWEB_LOG_ERROR << this << " Response was ended twice";
19004e438cbSEd Tanous             return;
19104e438cbSEd Tanous         }
19204e438cbSEd Tanous         completed = true;
19372374eb7SNan Zhou         BMCWEB_LOG_DEBUG << this << " calling completion handler";
19404e438cbSEd Tanous         if (completeRequestHandler)
19504e438cbSEd Tanous         {
19672374eb7SNan Zhou             BMCWEB_LOG_DEBUG << this << " completion handler was valid";
19772374eb7SNan Zhou             completeRequestHandler(*this);
19804e438cbSEd Tanous         }
19904e438cbSEd Tanous     }
20004e438cbSEd Tanous 
201bb60f4deSEd Tanous     bool isAlive() const
20204e438cbSEd Tanous     {
20304e438cbSEd Tanous         return isAliveHelper && isAliveHelper();
20404e438cbSEd Tanous     }
20504e438cbSEd Tanous 
20672374eb7SNan Zhou     void setCompleteRequestHandler(std::function<void(Response&)>&& handler)
2074147b8acSJohn Edward Broadbent     {
20872374eb7SNan Zhou         BMCWEB_LOG_DEBUG << this << " setting completion handler";
20972374eb7SNan Zhou         completeRequestHandler = std::move(handler);
21013548d85SEd Tanous 
21113548d85SEd Tanous         // Now that we have a new completion handler attached, we're no longer
21213548d85SEd Tanous         // complete
21313548d85SEd Tanous         completed = false;
21472374eb7SNan Zhou     }
21572374eb7SNan Zhou 
21672374eb7SNan Zhou     std::function<void(Response&)> releaseCompleteRequestHandler()
21772374eb7SNan Zhou     {
21872374eb7SNan Zhou         BMCWEB_LOG_DEBUG << this << " releasing completion handler"
21972374eb7SNan Zhou                          << static_cast<bool>(completeRequestHandler);
22072374eb7SNan Zhou         std::function<void(Response&)> ret = completeRequestHandler;
22172374eb7SNan Zhou         completeRequestHandler = nullptr;
22213548d85SEd Tanous         completed = true;
22372374eb7SNan Zhou         return ret;
22472374eb7SNan Zhou     }
22572374eb7SNan Zhou 
22672374eb7SNan Zhou     void setIsAliveHelper(std::function<bool()>&& handler)
22772374eb7SNan Zhou     {
22872374eb7SNan Zhou         isAliveHelper = std::move(handler);
22972374eb7SNan Zhou     }
23072374eb7SNan Zhou 
23172374eb7SNan Zhou     std::function<bool()> releaseIsAliveHelper()
23272374eb7SNan Zhou     {
23372374eb7SNan Zhou         std::function<bool()> ret = std::move(isAliveHelper);
23472374eb7SNan Zhou         isAliveHelper = nullptr;
23572374eb7SNan Zhou         return ret;
2364147b8acSJohn Edward Broadbent     }
2374147b8acSJohn Edward Broadbent 
238291d709dSEd Tanous     void setHashAndHandleNotModified()
239291d709dSEd Tanous     {
240291d709dSEd Tanous         // Can only hash if we have content that's valid
241291d709dSEd Tanous         if (jsonValue.empty() || result() != boost::beast::http::status::ok)
242291d709dSEd Tanous         {
243291d709dSEd Tanous             return;
244291d709dSEd Tanous         }
245291d709dSEd Tanous         size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
246291d709dSEd Tanous         std::string hexVal = "\"" + intToHexString(hashval, 8) + "\"";
247291d709dSEd Tanous         addHeader(boost::beast::http::field::etag, hexVal);
248291d709dSEd Tanous         if (expectedHash && hexVal == *expectedHash)
249291d709dSEd Tanous         {
250291d709dSEd Tanous             jsonValue.clear();
251291d709dSEd Tanous             result(boost::beast::http::status::not_modified);
252291d709dSEd Tanous         }
253291d709dSEd Tanous     }
254291d709dSEd Tanous 
255291d709dSEd Tanous     void setExpectedHash(std::string_view hash)
256291d709dSEd Tanous     {
257291d709dSEd Tanous         expectedHash = hash;
258291d709dSEd Tanous     }
259291d709dSEd Tanous 
26004e438cbSEd Tanous   private:
261291d709dSEd Tanous     std::optional<std::string> expectedHash;
26272374eb7SNan Zhou     bool completed = false;
26372374eb7SNan Zhou     std::function<void(Response&)> completeRequestHandler;
26404e438cbSEd Tanous     std::function<bool()> isAliveHelper;
26504e438cbSEd Tanous };
26604e438cbSEd Tanous } // namespace crow
267