xref: /openbmc/bmcweb/http/http_response.hpp (revision 89f180089bce9cc431d0b1053410f262f157b987)
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>
7*89f18008SEd 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 
43ecd6a3a2SEd Tanous     ~Response() = default;
44ecd6a3a2SEd Tanous 
45ecd6a3a2SEd Tanous     Response(const Response&) = delete;
46ecd6a3a2SEd Tanous     Response(Response&&) = delete;
4772374eb7SNan Zhou 
4804e438cbSEd Tanous     Response& operator=(const Response& r) = delete;
4904e438cbSEd Tanous 
5004e438cbSEd Tanous     Response& operator=(Response&& r) noexcept
5104e438cbSEd Tanous     {
5272374eb7SNan Zhou         BMCWEB_LOG_DEBUG << "Moving response containers; this: " << this
5372374eb7SNan Zhou                          << "; other: " << &r;
5472374eb7SNan Zhou         if (this == &r)
5572374eb7SNan Zhou         {
5672374eb7SNan Zhou             return *this;
5772374eb7SNan Zhou         }
5804e438cbSEd Tanous         stringResponse = std::move(r.stringResponse);
5904e438cbSEd Tanous         r.stringResponse.emplace(response_type{});
6004e438cbSEd Tanous         jsonValue = std::move(r.jsonValue);
6104e438cbSEd Tanous         completed = r.completed;
6272374eb7SNan Zhou         completeRequestHandler = std::move(r.completeRequestHandler);
6372374eb7SNan Zhou         isAliveHelper = std::move(r.isAliveHelper);
6472374eb7SNan Zhou         r.completeRequestHandler = nullptr;
6572374eb7SNan Zhou         r.isAliveHelper = nullptr;
6604e438cbSEd Tanous         return *this;
6704e438cbSEd Tanous     }
6804e438cbSEd Tanous 
6904e438cbSEd Tanous     void result(boost::beast::http::status v)
7004e438cbSEd Tanous     {
7104e438cbSEd Tanous         stringResponse->result(v);
7204e438cbSEd Tanous     }
7304e438cbSEd Tanous 
7404e438cbSEd Tanous     boost::beast::http::status result()
7504e438cbSEd Tanous     {
7604e438cbSEd Tanous         return stringResponse->result();
7704e438cbSEd Tanous     }
7804e438cbSEd Tanous 
7904e438cbSEd Tanous     unsigned resultInt()
8004e438cbSEd Tanous     {
8104e438cbSEd Tanous         return stringResponse->result_int();
8204e438cbSEd Tanous     }
8304e438cbSEd Tanous 
8404e438cbSEd Tanous     std::string_view reason()
8504e438cbSEd Tanous     {
8604e438cbSEd Tanous         return stringResponse->reason();
8704e438cbSEd Tanous     }
8804e438cbSEd Tanous 
8904e438cbSEd Tanous     bool isCompleted() const noexcept
9004e438cbSEd Tanous     {
9104e438cbSEd Tanous         return completed;
9204e438cbSEd Tanous     }
9304e438cbSEd Tanous 
9404e438cbSEd Tanous     std::string& body()
9504e438cbSEd Tanous     {
9604e438cbSEd Tanous         return stringResponse->body();
9704e438cbSEd Tanous     }
9804e438cbSEd Tanous 
9904e438cbSEd Tanous     void keepAlive(bool k)
10004e438cbSEd Tanous     {
10104e438cbSEd Tanous         stringResponse->keep_alive(k);
10204e438cbSEd Tanous     }
10304e438cbSEd Tanous 
10404e438cbSEd Tanous     bool keepAlive()
10504e438cbSEd Tanous     {
10604e438cbSEd Tanous         return stringResponse->keep_alive();
10704e438cbSEd Tanous     }
10804e438cbSEd Tanous 
10904e438cbSEd Tanous     void preparePayload()
11004e438cbSEd Tanous     {
11104e438cbSEd Tanous         stringResponse->prepare_payload();
11204e438cbSEd Tanous     }
11304e438cbSEd Tanous 
11404e438cbSEd Tanous     void clear()
11504e438cbSEd Tanous     {
11604e438cbSEd Tanous         BMCWEB_LOG_DEBUG << this << " Clearing response containers";
11704e438cbSEd Tanous         stringResponse.emplace(response_type{});
11804e438cbSEd Tanous         jsonValue.clear();
11904e438cbSEd Tanous         completed = false;
12004e438cbSEd Tanous     }
12104e438cbSEd Tanous 
12281ce609eSEd Tanous     void write(std::string_view bodyPart)
12304e438cbSEd Tanous     {
12481ce609eSEd Tanous         stringResponse->body() += std::string(bodyPart);
12504e438cbSEd Tanous     }
12604e438cbSEd Tanous 
12704e438cbSEd Tanous     void end()
12804e438cbSEd Tanous     {
129*89f18008SEd Tanous         // Only set etag if this request succeeded
130*89f18008SEd Tanous         if (result() == boost::beast::http::status::ok)
131*89f18008SEd Tanous         {
132*89f18008SEd Tanous             // and the json response isn't empty
133*89f18008SEd Tanous             if (!jsonValue.empty())
134*89f18008SEd Tanous             {
135*89f18008SEd Tanous                 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
136*89f18008SEd Tanous                 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\"";
137*89f18008SEd Tanous                 addHeader(boost::beast::http::field::etag, hexVal);
138*89f18008SEd Tanous             }
139*89f18008SEd Tanous         }
14004e438cbSEd Tanous         if (completed)
14104e438cbSEd Tanous         {
14272374eb7SNan Zhou             BMCWEB_LOG_ERROR << this << " Response was ended twice";
14304e438cbSEd Tanous             return;
14404e438cbSEd Tanous         }
14504e438cbSEd Tanous         completed = true;
14672374eb7SNan Zhou         BMCWEB_LOG_DEBUG << this << " calling completion handler";
14704e438cbSEd Tanous         if (completeRequestHandler)
14804e438cbSEd Tanous         {
14972374eb7SNan Zhou             BMCWEB_LOG_DEBUG << this << " completion handler was valid";
15072374eb7SNan Zhou             completeRequestHandler(*this);
15104e438cbSEd Tanous         }
15204e438cbSEd Tanous     }
15304e438cbSEd Tanous 
15404e438cbSEd Tanous     bool isAlive()
15504e438cbSEd Tanous     {
15604e438cbSEd Tanous         return isAliveHelper && isAliveHelper();
15704e438cbSEd Tanous     }
15804e438cbSEd Tanous 
15972374eb7SNan Zhou     void setCompleteRequestHandler(std::function<void(Response&)>&& handler)
1604147b8acSJohn Edward Broadbent     {
16172374eb7SNan Zhou         BMCWEB_LOG_DEBUG << this << " setting completion handler";
16272374eb7SNan Zhou         completeRequestHandler = std::move(handler);
16372374eb7SNan Zhou     }
16472374eb7SNan Zhou 
16572374eb7SNan Zhou     std::function<void(Response&)> releaseCompleteRequestHandler()
16672374eb7SNan Zhou     {
16772374eb7SNan Zhou         BMCWEB_LOG_DEBUG << this << " releasing completion handler"
16872374eb7SNan Zhou                          << static_cast<bool>(completeRequestHandler);
16972374eb7SNan Zhou         std::function<void(Response&)> ret = completeRequestHandler;
17072374eb7SNan Zhou         completeRequestHandler = nullptr;
17172374eb7SNan Zhou         return ret;
17272374eb7SNan Zhou     }
17372374eb7SNan Zhou 
17472374eb7SNan Zhou     void setIsAliveHelper(std::function<bool()>&& handler)
17572374eb7SNan Zhou     {
17672374eb7SNan Zhou         isAliveHelper = std::move(handler);
17772374eb7SNan Zhou     }
17872374eb7SNan Zhou 
17972374eb7SNan Zhou     std::function<bool()> releaseIsAliveHelper()
18072374eb7SNan Zhou     {
18172374eb7SNan Zhou         std::function<bool()> ret = std::move(isAliveHelper);
18272374eb7SNan Zhou         isAliveHelper = nullptr;
18372374eb7SNan Zhou         return ret;
1844147b8acSJohn Edward Broadbent     }
1854147b8acSJohn Edward Broadbent 
18604e438cbSEd Tanous   private:
18772374eb7SNan Zhou     bool completed = false;
18872374eb7SNan Zhou     std::function<void(Response&)> completeRequestHandler;
18904e438cbSEd Tanous     std::function<bool()> isAliveHelper;
19004e438cbSEd Tanous 
19104e438cbSEd Tanous     // In case of a JSON object, set the Content-Type header
19204e438cbSEd Tanous     void jsonMode()
19304e438cbSEd Tanous     {
19404e438cbSEd Tanous         addHeader("Content-Type", "application/json");
19504e438cbSEd Tanous     }
19604e438cbSEd Tanous };
19704e438cbSEd Tanous } // namespace crow
198