xref: /openbmc/bmcweb/http/http_response.hpp (revision 291d709d3505cb345259e7d78eb6c8b765499c1b)
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