xref: /openbmc/bmcweb/http/http_response.hpp (revision 852432ac)
1 #pragma once
2 #include "logging.hpp"
3 #include "nlohmann/json.hpp"
4 
5 #include <boost/beast/http/message.hpp>
6 #include <boost/beast/http/string_body.hpp>
7 #include <utils/hex_utils.hpp>
8 
9 #include <optional>
10 #include <string>
11 #include <string_view>
12 
13 namespace crow
14 {
15 
16 template <typename Adaptor, typename Handler>
17 class Connection;
18 
19 struct Response
20 {
21     template <typename Adaptor, typename Handler>
22     friend class crow::Connection;
23     using response_type =
24         boost::beast::http::response<boost::beast::http::string_body>;
25 
26     std::optional<response_type> stringResponse;
27 
28     nlohmann::json jsonValue;
29 
30     void addHeader(const std::string_view key, const std::string_view value)
31     {
32         stringResponse->set(key, value);
33     }
34 
35     void addHeader(boost::beast::http::field key, std::string_view value)
36     {
37         stringResponse->set(key, value);
38     }
39 
40     Response() : stringResponse(response_type{})
41     {}
42 
43     ~Response() = default;
44 
45     Response(const Response&) = delete;
46     Response(Response&&) = delete;
47 
48     Response& operator=(const Response& r) = delete;
49 
50     Response& operator=(Response&& r) noexcept
51     {
52         BMCWEB_LOG_DEBUG << "Moving response containers; this: " << this
53                          << "; other: " << &r;
54         if (this == &r)
55         {
56             return *this;
57         }
58         stringResponse = std::move(r.stringResponse);
59         r.stringResponse.emplace(response_type{});
60         jsonValue = std::move(r.jsonValue);
61         completed = r.completed;
62         completeRequestHandler = std::move(r.completeRequestHandler);
63         isAliveHelper = std::move(r.isAliveHelper);
64         r.completeRequestHandler = nullptr;
65         r.isAliveHelper = nullptr;
66         return *this;
67     }
68 
69     void result(boost::beast::http::status v)
70     {
71         stringResponse->result(v);
72     }
73 
74     boost::beast::http::status result() const
75     {
76         return stringResponse->result();
77     }
78 
79     unsigned resultInt() const
80     {
81         return stringResponse->result_int();
82     }
83 
84     std::string_view reason() const
85     {
86         return stringResponse->reason();
87     }
88 
89     bool isCompleted() const noexcept
90     {
91         return completed;
92     }
93 
94     std::string& body()
95     {
96         return stringResponse->body();
97     }
98 
99     void keepAlive(bool k)
100     {
101         stringResponse->keep_alive(k);
102     }
103 
104     bool keepAlive() const
105     {
106         return stringResponse->keep_alive();
107     }
108 
109     void preparePayload()
110     {
111         stringResponse->prepare_payload();
112     }
113 
114     void clear()
115     {
116         BMCWEB_LOG_DEBUG << this << " Clearing response containers";
117         stringResponse.emplace(response_type{});
118         jsonValue.clear();
119         completed = false;
120     }
121 
122     void write(std::string_view bodyPart)
123     {
124         stringResponse->body() += std::string(bodyPart);
125     }
126 
127     void end()
128     {
129         // Only set etag if this request succeeded
130         if (result() == boost::beast::http::status::ok)
131         {
132             // and the json response isn't empty
133             if (!jsonValue.empty())
134             {
135                 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
136                 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\"";
137                 addHeader(boost::beast::http::field::etag, hexVal);
138             }
139         }
140         if (completed)
141         {
142             BMCWEB_LOG_ERROR << this << " Response was ended twice";
143             return;
144         }
145         completed = true;
146         BMCWEB_LOG_DEBUG << this << " calling completion handler";
147         if (completeRequestHandler)
148         {
149             BMCWEB_LOG_DEBUG << this << " completion handler was valid";
150             completeRequestHandler(*this);
151         }
152     }
153 
154     bool isAlive() const
155     {
156         return isAliveHelper && isAliveHelper();
157     }
158 
159     void setCompleteRequestHandler(std::function<void(Response&)>&& handler)
160     {
161         BMCWEB_LOG_DEBUG << this << " setting completion handler";
162         completeRequestHandler = std::move(handler);
163     }
164 
165     std::function<void(Response&)> releaseCompleteRequestHandler()
166     {
167         BMCWEB_LOG_DEBUG << this << " releasing completion handler"
168                          << static_cast<bool>(completeRequestHandler);
169         std::function<void(Response&)> ret = completeRequestHandler;
170         completeRequestHandler = nullptr;
171         return ret;
172     }
173 
174     void setIsAliveHelper(std::function<bool()>&& handler)
175     {
176         isAliveHelper = std::move(handler);
177     }
178 
179     std::function<bool()> releaseIsAliveHelper()
180     {
181         std::function<bool()> ret = std::move(isAliveHelper);
182         isAliveHelper = nullptr;
183         return ret;
184     }
185 
186   private:
187     bool completed = false;
188     std::function<void(Response&)> completeRequestHandler;
189     std::function<bool()> isAliveHelper;
190 
191     // In case of a JSON object, set the Content-Type header
192     void jsonMode()
193     {
194         addHeader("Content-Type", "application/json");
195     }
196 };
197 } // namespace crow
198