blob: c4f9366ed3595c3e49f0d7d070e22e565d2e6da5 [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 Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "utils/hex_utils.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -07004
Ed Tanousd43cd0c2020-09-30 20:46:53 -07005#include <boost/beast/http/message.hpp>
Ed Tanousd4b6c662021-03-10 13:29:30 -08006#include <boost/beast/http/string_body.hpp>
Ed Tanousfaf100f2023-05-25 10:03:14 -07007#include <nlohmann/json.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 Tanous26ccae32023-02-16 10:28:44 -080030 void addHeader(std::string_view key, std::string_view value)
Ed Tanous1abe55e2018-09-05 08:30:59 -070031 {
Ed Tanous994fd862023-06-06 13:37:03 -070032 stringResponse->insert(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 {
Ed Tanous994fd862023-06-06 13:37:03 -070037 stringResponse->insert(key, value);
38 }
39
40 void clearHeader(boost::beast::http::field key)
41 {
42 stringResponse->erase(key);
Ed Tanous2cd4cc12018-07-25 10:51:19 -070043 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070044
Patrick Williams89492a12023-05-10 07:51:34 -050045 Response() : stringResponse(response_type{}) {}
Ed Tanous7045c8d2017-04-03 10:04:37 -070046
Ed Tanous13548d82022-07-22 09:50:44 -070047 Response(Response&& res) noexcept :
Ed Tanousf8fe53e2022-06-30 15:55:45 -070048 stringResponse(std::move(res.stringResponse)),
49 jsonValue(std::move(res.jsonValue)), completed(res.completed)
Ed Tanous13548d82022-07-22 09:50:44 -070050 {
Ed Tanous13548d82022-07-22 09:50:44 -070051 // See note in operator= move handler for why this is needed.
52 if (!res.completed)
53 {
54 completeRequestHandler = std::move(res.completeRequestHandler);
55 res.completeRequestHandler = nullptr;
56 }
57 isAliveHelper = res.isAliveHelper;
58 res.isAliveHelper = nullptr;
59 }
60
Ed Tanousecd6a3a2022-01-07 09:18:40 -080061 ~Response() = default;
62
63 Response(const Response&) = delete;
Nan Zhou72374eb2022-01-27 17:06:51 -080064
Ed Tanous1abe55e2018-09-05 08:30:59 -070065 Response& operator=(const Response& r) = delete;
66
67 Response& operator=(Response&& r) noexcept
68 {
Ed Tanous62598e32023-07-17 17:06:25 -070069 BMCWEB_LOG_DEBUG("Moving response containers; this: {}; other: {}",
70 logPtr(this), logPtr(&r));
Nan Zhou72374eb2022-01-27 17:06:51 -080071 if (this == &r)
72 {
73 return *this;
74 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070075 stringResponse = std::move(r.stringResponse);
76 r.stringResponse.emplace(response_type{});
77 jsonValue = std::move(r.jsonValue);
Ed Tanous13548d82022-07-22 09:50:44 -070078
79 // Only need to move completion handler if not already completed
80 // Note, there are cases where we might move out of a Response object
81 // while in a completion handler for that response object. This check
82 // is intended to prevent destructing the functor we are currently
83 // executing from in that case.
84 if (!r.completed)
85 {
86 completeRequestHandler = std::move(r.completeRequestHandler);
87 r.completeRequestHandler = nullptr;
88 }
89 else
90 {
91 completeRequestHandler = nullptr;
92 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070093 completed = r.completed;
Nan Zhou72374eb2022-01-27 17:06:51 -080094 isAliveHelper = std::move(r.isAliveHelper);
Nan Zhou72374eb2022-01-27 17:06:51 -080095 r.isAliveHelper = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -070096 return *this;
97 }
98
Nan Zhou3590bd12022-08-12 18:05:09 +000099 void result(unsigned v)
100 {
101 stringResponse->result(v);
102 }
103
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104 void result(boost::beast::http::status v)
105 {
106 stringResponse->result(v);
107 }
108
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700109 boost::beast::http::status result() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700110 {
111 return stringResponse->result();
112 }
113
Carson Labrado039a47e2022-04-05 16:03:20 +0000114 unsigned resultInt() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 {
116 return stringResponse->result_int();
117 }
118
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700119 std::string_view reason() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 {
121 return stringResponse->reason();
122 }
123
124 bool isCompleted() const noexcept
125 {
126 return completed;
127 }
128
129 std::string& body()
130 {
131 return stringResponse->body();
132 }
133
Carson Labrado46a81462022-04-27 21:11:37 +0000134 std::string_view getHeaderValue(std::string_view key) const
135 {
136 return stringResponse->base()[key];
137 }
138
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 void keepAlive(bool k)
140 {
141 stringResponse->keep_alive(k);
142 }
143
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700144 bool keepAlive() const
Ed Tanousceac6f72018-12-02 11:58:47 -0800145 {
146 return stringResponse->keep_alive();
147 }
148
Ed Tanous1abe55e2018-09-05 08:30:59 -0700149 void preparePayload()
150 {
Ed Tanouseea9c972023-05-15 11:44:27 -0700151 // This code is a throw-free equivalent to
152 // beast::http::message::prepare_payload
153 boost::optional<uint64_t> pSize = stringResponse->payload_size();
154 using boost::beast::http::status;
155 using boost::beast::http::status_class;
156 using boost::beast::http::to_status_class;
157 if (!pSize)
158 {
159 pSize = 0;
160 }
161 else
162 {
163 bool is1XXReturn = to_status_class(stringResponse->result()) ==
164 status_class::informational;
165 if (*pSize > 0 &&
166 (is1XXReturn ||
167 stringResponse->result() == status::no_content ||
168 stringResponse->result() == status::not_modified))
169 {
Ed Tanous62598e32023-07-17 17:06:25 -0700170 BMCWEB_LOG_CRITICAL(
171 "{} Response content provided but code was no-content or not_modified, which aren't allowed to have a body",
172 logPtr(this));
Ed Tanouseea9c972023-05-15 11:44:27 -0700173 pSize = 0;
174 body().clear();
175 }
176 }
177 stringResponse->content_length(*pSize);
Ed Tanous23a21a12020-07-25 04:45:05 +0000178 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700179
180 void clear()
181 {
Ed Tanous62598e32023-07-17 17:06:25 -0700182 BMCWEB_LOG_DEBUG("{} Clearing response containers", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700183 stringResponse.emplace(response_type{});
Ed Tanousa6695a82023-05-16 11:39:18 -0700184 jsonValue = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700185 completed = false;
Ed Tanous291d7092022-04-13 12:34:57 -0700186 expectedHash = std::nullopt;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700187 }
188
Ed Tanous81ce6092020-12-17 16:54:55 +0000189 void write(std::string_view bodyPart)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700190 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000191 stringResponse->body() += std::string(bodyPart);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700192 }
193
Ed Tanous2d6cb562022-07-07 20:44:54 -0700194 std::string computeEtag() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700195 {
Ed Tanous89f18002022-03-24 18:38:24 -0700196 // Only set etag if this request succeeded
Ed Tanous2d6cb562022-07-07 20:44:54 -0700197 if (result() != boost::beast::http::status::ok)
Ed Tanous89f18002022-03-24 18:38:24 -0700198 {
Ed Tanous2d6cb562022-07-07 20:44:54 -0700199 return "";
200 }
201 // and the json response isn't empty
202 if (jsonValue.empty())
203 {
204 return "";
205 }
206 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
207 return "\"" + intToHexString(hashval, 8) + "\"";
208 }
209
210 void end()
211 {
212 std::string etag = computeEtag();
213 if (!etag.empty())
214 {
215 addHeader(boost::beast::http::field::etag, etag);
Ed Tanous89f18002022-03-24 18:38:24 -0700216 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700217 if (completed)
218 {
Ed Tanous62598e32023-07-17 17:06:25 -0700219 BMCWEB_LOG_ERROR("{} Response was ended twice", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700220 return;
221 }
222 completed = true;
Ed Tanous62598e32023-07-17 17:06:25 -0700223 BMCWEB_LOG_DEBUG("{} calling completion handler", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700224 if (completeRequestHandler)
225 {
Ed Tanous62598e32023-07-17 17:06:25 -0700226 BMCWEB_LOG_DEBUG("{} completion handler was valid", logPtr(this));
Nan Zhou72374eb2022-01-27 17:06:51 -0800227 completeRequestHandler(*this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228 }
229 }
230
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700231 bool isAlive() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700232 {
233 return isAliveHelper && isAliveHelper();
234 }
235
Nan Zhou72374eb2022-01-27 17:06:51 -0800236 void setCompleteRequestHandler(std::function<void(Response&)>&& handler)
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700237 {
Ed Tanous62598e32023-07-17 17:06:25 -0700238 BMCWEB_LOG_DEBUG("{} setting completion handler", logPtr(this));
Nan Zhou72374eb2022-01-27 17:06:51 -0800239 completeRequestHandler = std::move(handler);
Ed Tanous13548d82022-07-22 09:50:44 -0700240
241 // Now that we have a new completion handler attached, we're no longer
242 // complete
243 completed = false;
Nan Zhou72374eb2022-01-27 17:06:51 -0800244 }
245
246 std::function<void(Response&)> releaseCompleteRequestHandler()
247 {
Ed Tanous62598e32023-07-17 17:06:25 -0700248 BMCWEB_LOG_DEBUG("{} releasing completion handler{}", logPtr(this),
249 static_cast<bool>(completeRequestHandler));
Nan Zhou72374eb2022-01-27 17:06:51 -0800250 std::function<void(Response&)> ret = completeRequestHandler;
251 completeRequestHandler = nullptr;
Ed Tanous13548d82022-07-22 09:50:44 -0700252 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800253 return ret;
254 }
255
256 void setIsAliveHelper(std::function<bool()>&& handler)
257 {
258 isAliveHelper = std::move(handler);
259 }
260
261 std::function<bool()> releaseIsAliveHelper()
262 {
263 std::function<bool()> ret = std::move(isAliveHelper);
264 isAliveHelper = nullptr;
265 return ret;
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700266 }
267
Ed Tanous291d7092022-04-13 12:34:57 -0700268 void setHashAndHandleNotModified()
269 {
270 // Can only hash if we have content that's valid
271 if (jsonValue.empty() || result() != boost::beast::http::status::ok)
272 {
273 return;
274 }
275 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
276 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\"";
277 addHeader(boost::beast::http::field::etag, hexVal);
278 if (expectedHash && hexVal == *expectedHash)
279 {
Ed Tanousa6695a82023-05-16 11:39:18 -0700280 jsonValue = nullptr;
Ed Tanous291d7092022-04-13 12:34:57 -0700281 result(boost::beast::http::status::not_modified);
282 }
283 }
284
285 void setExpectedHash(std::string_view hash)
286 {
287 expectedHash = hash;
288 }
289
Ed Tanous1abe55e2018-09-05 08:30:59 -0700290 private:
Ed Tanous291d7092022-04-13 12:34:57 -0700291 std::optional<std::string> expectedHash;
Nan Zhou72374eb2022-01-27 17:06:51 -0800292 bool completed = false;
293 std::function<void(Response&)> completeRequestHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 std::function<bool()> isAliveHelper;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700295};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700296} // namespace crow