blob: f9204e4a4e953ba6eedcde85d17a49051a1da558 [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
Ed Tanous04e438c2020-10-03 08:06:26 -07002#include "logging.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -07003#include "nlohmann/json.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08004#include "utils/hex_utils.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -07005
Ed Tanousd43cd0c2020-09-30 20:46:53 -07006#include <boost/beast/http/message.hpp>
Ed Tanousd4b6c662021-03-10 13:29:30 -08007#include <boost/beast/http/string_body.hpp>
Ed Tanous7045c8d2017-04-03 10:04:37 -07008
Ed Tanous8a9a25c2021-05-11 14:50:58 -07009#include <optional>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050010#include <string>
Ed Tanous8a9a25c2021-05-11 14:50:58 -070011#include <string_view>
Ed Tanous7045c8d2017-04-03 10:04:37 -070012
Ed Tanous1abe55e2018-09-05 08:30:59 -070013namespace crow
14{
Ed Tanouse0d918b2018-03-27 17:41:04 -070015
Ed Tanous52cc1122020-07-18 13:51:21 -070016template <typename Adaptor, typename Handler>
Ed Tanous7045c8d2017-04-03 10:04:37 -070017class Connection;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010018
Ed Tanous1abe55e2018-09-05 08:30:59 -070019struct Response
20{
Ed Tanous52cc1122020-07-18 13:51:21 -070021 template <typename Adaptor, typename Handler>
Ed Tanous1abe55e2018-09-05 08:30:59 -070022 friend class crow::Connection;
23 using response_type =
24 boost::beast::http::response<boost::beast::http::string_body>;
Ed Tanous7045c8d2017-04-03 10:04:37 -070025
Ed Tanousa24526d2018-12-10 15:17:59 -080026 std::optional<response_type> stringResponse;
Ed Tanouse0d918b2018-03-27 17:41:04 -070027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028 nlohmann::json jsonValue;
Ed Tanous7045c8d2017-04-03 10:04:37 -070029
Ed Tanous39e77502019-03-04 17:35:53 -080030 void addHeader(const std::string_view key, const std::string_view value)
Ed Tanous1abe55e2018-09-05 08:30:59 -070031 {
32 stringResponse->set(key, value);
Ed Tanous7045c8d2017-04-03 10:04:37 -070033 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070034
Ed Tanous39e77502019-03-04 17:35:53 -080035 void addHeader(boost::beast::http::field key, std::string_view value)
Ed Tanous1abe55e2018-09-05 08:30:59 -070036 {
37 stringResponse->set(key, value);
Ed Tanous2cd4cc12018-07-25 10:51:19 -070038 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070039
Ed Tanous1abe55e2018-09-05 08:30:59 -070040 Response() : stringResponse(response_type{})
Gunnar Mills1214b7e2020-06-04 10:11:30 -050041 {}
Ed Tanous7045c8d2017-04-03 10:04:37 -070042
Ed Tanous13548d82022-07-22 09:50:44 -070043 Response(Response&& res) noexcept :
Ed Tanousf8fe53e2022-06-30 15:55:45 -070044 stringResponse(std::move(res.stringResponse)),
45 jsonValue(std::move(res.jsonValue)), completed(res.completed)
Ed Tanous13548d82022-07-22 09:50:44 -070046 {
Ed Tanous13548d82022-07-22 09:50:44 -070047 // See note in operator= move handler for why this is needed.
48 if (!res.completed)
49 {
50 completeRequestHandler = std::move(res.completeRequestHandler);
51 res.completeRequestHandler = nullptr;
52 }
53 isAliveHelper = res.isAliveHelper;
54 res.isAliveHelper = nullptr;
55 }
56
Ed Tanousecd6a3a2022-01-07 09:18:40 -080057 ~Response() = default;
58
59 Response(const Response&) = delete;
Nan Zhou72374eb2022-01-27 17:06:51 -080060
Ed Tanous1abe55e2018-09-05 08:30:59 -070061 Response& operator=(const Response& r) = delete;
62
63 Response& operator=(Response&& r) noexcept
64 {
Nan Zhou72374eb2022-01-27 17:06:51 -080065 BMCWEB_LOG_DEBUG << "Moving response containers; this: " << this
66 << "; other: " << &r;
67 if (this == &r)
68 {
69 return *this;
70 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070071 stringResponse = std::move(r.stringResponse);
72 r.stringResponse.emplace(response_type{});
73 jsonValue = std::move(r.jsonValue);
Ed Tanous13548d82022-07-22 09:50:44 -070074
75 // Only need to move completion handler if not already completed
76 // Note, there are cases where we might move out of a Response object
77 // while in a completion handler for that response object. This check
78 // is intended to prevent destructing the functor we are currently
79 // executing from in that case.
80 if (!r.completed)
81 {
82 completeRequestHandler = std::move(r.completeRequestHandler);
83 r.completeRequestHandler = nullptr;
84 }
85 else
86 {
87 completeRequestHandler = nullptr;
88 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070089 completed = r.completed;
Nan Zhou72374eb2022-01-27 17:06:51 -080090 isAliveHelper = std::move(r.isAliveHelper);
Nan Zhou72374eb2022-01-27 17:06:51 -080091 r.isAliveHelper = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -070092 return *this;
93 }
94
Nan Zhou3590bd12022-08-12 18:05:09 +000095 void result(unsigned v)
96 {
97 stringResponse->result(v);
98 }
99
Ed Tanous1abe55e2018-09-05 08:30:59 -0700100 void result(boost::beast::http::status v)
101 {
102 stringResponse->result(v);
103 }
104
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700105 boost::beast::http::status result() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 {
107 return stringResponse->result();
108 }
109
Carson Labrado039a47e2022-04-05 16:03:20 +0000110 unsigned resultInt() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111 {
112 return stringResponse->result_int();
113 }
114
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700115 std::string_view reason() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116 {
117 return stringResponse->reason();
118 }
119
120 bool isCompleted() const noexcept
121 {
122 return completed;
123 }
124
125 std::string& body()
126 {
127 return stringResponse->body();
128 }
129
Carson Labrado46a81462022-04-27 21:11:37 +0000130 std::string_view getHeaderValue(std::string_view key) const
131 {
132 return stringResponse->base()[key];
133 }
134
Ed Tanous1abe55e2018-09-05 08:30:59 -0700135 void keepAlive(bool k)
136 {
137 stringResponse->keep_alive(k);
138 }
139
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700140 bool keepAlive() const
Ed Tanousceac6f72018-12-02 11:58:47 -0800141 {
142 return stringResponse->keep_alive();
143 }
144
Ed Tanous1abe55e2018-09-05 08:30:59 -0700145 void preparePayload()
146 {
147 stringResponse->prepare_payload();
Ed Tanous23a21a12020-07-25 04:45:05 +0000148 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700149
150 void clear()
151 {
152 BMCWEB_LOG_DEBUG << this << " Clearing response containers";
153 stringResponse.emplace(response_type{});
154 jsonValue.clear();
155 completed = false;
Ed Tanous291d7092022-04-13 12:34:57 -0700156 expectedHash = std::nullopt;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700157 }
158
Ed Tanous81ce6092020-12-17 16:54:55 +0000159 void write(std::string_view bodyPart)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700160 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000161 stringResponse->body() += std::string(bodyPart);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700162 }
163
Ed Tanous2d6cb562022-07-07 20:44:54 -0700164 std::string computeEtag() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700165 {
Ed Tanous89f18002022-03-24 18:38:24 -0700166 // Only set etag if this request succeeded
Ed Tanous2d6cb562022-07-07 20:44:54 -0700167 if (result() != boost::beast::http::status::ok)
Ed Tanous89f18002022-03-24 18:38:24 -0700168 {
Ed Tanous2d6cb562022-07-07 20:44:54 -0700169 return "";
170 }
171 // and the json response isn't empty
172 if (jsonValue.empty())
173 {
174 return "";
175 }
176 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
177 return "\"" + intToHexString(hashval, 8) + "\"";
178 }
179
180 void end()
181 {
182 std::string etag = computeEtag();
183 if (!etag.empty())
184 {
185 addHeader(boost::beast::http::field::etag, etag);
Ed Tanous89f18002022-03-24 18:38:24 -0700186 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700187 if (completed)
188 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800189 BMCWEB_LOG_ERROR << this << " Response was ended twice";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700190 return;
191 }
192 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800193 BMCWEB_LOG_DEBUG << this << " calling completion handler";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700194 if (completeRequestHandler)
195 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800196 BMCWEB_LOG_DEBUG << this << " completion handler was valid";
197 completeRequestHandler(*this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700198 }
199 }
200
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700201 bool isAlive() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700202 {
203 return isAliveHelper && isAliveHelper();
204 }
205
Nan Zhou72374eb2022-01-27 17:06:51 -0800206 void setCompleteRequestHandler(std::function<void(Response&)>&& handler)
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700207 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800208 BMCWEB_LOG_DEBUG << this << " setting completion handler";
209 completeRequestHandler = std::move(handler);
Ed Tanous13548d82022-07-22 09:50:44 -0700210
211 // Now that we have a new completion handler attached, we're no longer
212 // complete
213 completed = false;
Nan Zhou72374eb2022-01-27 17:06:51 -0800214 }
215
216 std::function<void(Response&)> releaseCompleteRequestHandler()
217 {
218 BMCWEB_LOG_DEBUG << this << " releasing completion handler"
219 << static_cast<bool>(completeRequestHandler);
220 std::function<void(Response&)> ret = completeRequestHandler;
221 completeRequestHandler = nullptr;
Ed Tanous13548d82022-07-22 09:50:44 -0700222 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800223 return ret;
224 }
225
226 void setIsAliveHelper(std::function<bool()>&& handler)
227 {
228 isAliveHelper = std::move(handler);
229 }
230
231 std::function<bool()> releaseIsAliveHelper()
232 {
233 std::function<bool()> ret = std::move(isAliveHelper);
234 isAliveHelper = nullptr;
235 return ret;
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700236 }
237
Ed Tanous291d7092022-04-13 12:34:57 -0700238 void setHashAndHandleNotModified()
239 {
240 // Can only hash if we have content that's valid
241 if (jsonValue.empty() || result() != boost::beast::http::status::ok)
242 {
243 return;
244 }
245 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
246 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\"";
247 addHeader(boost::beast::http::field::etag, hexVal);
248 if (expectedHash && hexVal == *expectedHash)
249 {
250 jsonValue.clear();
251 result(boost::beast::http::status::not_modified);
252 }
253 }
254
255 void setExpectedHash(std::string_view hash)
256 {
257 expectedHash = hash;
258 }
259
Ed Tanous1abe55e2018-09-05 08:30:59 -0700260 private:
Ed Tanous291d7092022-04-13 12:34:57 -0700261 std::optional<std::string> expectedHash;
Nan Zhou72374eb2022-01-27 17:06:51 -0800262 bool completed = false;
263 std::function<void(Response&)> completeRequestHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700264 std::function<bool()> isAliveHelper;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700265};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700266} // namespace crow