blob: 1a4ef16d4e1ded278eed7284fca36965bd055445 [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 {
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
Patrick Williams89492a12023-05-10 07:51:34 -050040 Response() : stringResponse(response_type{}) {}
Ed Tanous7045c8d2017-04-03 10:04:37 -070041
Ed Tanous13548d82022-07-22 09:50:44 -070042 Response(Response&& res) noexcept :
Ed Tanousf8fe53e2022-06-30 15:55:45 -070043 stringResponse(std::move(res.stringResponse)),
44 jsonValue(std::move(res.jsonValue)), completed(res.completed)
Ed Tanous13548d82022-07-22 09:50:44 -070045 {
Ed Tanous13548d82022-07-22 09:50:44 -070046 // See note in operator= move handler for why this is needed.
47 if (!res.completed)
48 {
49 completeRequestHandler = std::move(res.completeRequestHandler);
50 res.completeRequestHandler = nullptr;
51 }
52 isAliveHelper = res.isAliveHelper;
53 res.isAliveHelper = nullptr;
54 }
55
Ed Tanousecd6a3a2022-01-07 09:18:40 -080056 ~Response() = default;
57
58 Response(const Response&) = delete;
Nan Zhou72374eb2022-01-27 17:06:51 -080059
Ed Tanous1abe55e2018-09-05 08:30:59 -070060 Response& operator=(const Response& r) = delete;
61
62 Response& operator=(Response&& r) noexcept
63 {
Nan Zhou72374eb2022-01-27 17:06:51 -080064 BMCWEB_LOG_DEBUG << "Moving response containers; this: " << this
65 << "; other: " << &r;
66 if (this == &r)
67 {
68 return *this;
69 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070070 stringResponse = std::move(r.stringResponse);
71 r.stringResponse.emplace(response_type{});
72 jsonValue = std::move(r.jsonValue);
Ed Tanous13548d82022-07-22 09:50:44 -070073
74 // Only need to move completion handler if not already completed
75 // Note, there are cases where we might move out of a Response object
76 // while in a completion handler for that response object. This check
77 // is intended to prevent destructing the functor we are currently
78 // executing from in that case.
79 if (!r.completed)
80 {
81 completeRequestHandler = std::move(r.completeRequestHandler);
82 r.completeRequestHandler = nullptr;
83 }
84 else
85 {
86 completeRequestHandler = nullptr;
87 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070088 completed = r.completed;
Nan Zhou72374eb2022-01-27 17:06:51 -080089 isAliveHelper = std::move(r.isAliveHelper);
Nan Zhou72374eb2022-01-27 17:06:51 -080090 r.isAliveHelper = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -070091 return *this;
92 }
93
Nan Zhou3590bd12022-08-12 18:05:09 +000094 void result(unsigned v)
95 {
96 stringResponse->result(v);
97 }
98
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 void result(boost::beast::http::status v)
100 {
101 stringResponse->result(v);
102 }
103
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700104 boost::beast::http::status result() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700105 {
106 return stringResponse->result();
107 }
108
Carson Labrado039a47e2022-04-05 16:03:20 +0000109 unsigned resultInt() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700110 {
111 return stringResponse->result_int();
112 }
113
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700114 std::string_view reason() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700115 {
116 return stringResponse->reason();
117 }
118
119 bool isCompleted() const noexcept
120 {
121 return completed;
122 }
123
124 std::string& body()
125 {
126 return stringResponse->body();
127 }
128
Carson Labrado46a81462022-04-27 21:11:37 +0000129 std::string_view getHeaderValue(std::string_view key) const
130 {
131 return stringResponse->base()[key];
132 }
133
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134 void keepAlive(bool k)
135 {
136 stringResponse->keep_alive(k);
137 }
138
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700139 bool keepAlive() const
Ed Tanousceac6f72018-12-02 11:58:47 -0800140 {
141 return stringResponse->keep_alive();
142 }
143
Ed Tanous1abe55e2018-09-05 08:30:59 -0700144 void preparePayload()
145 {
Ed Tanouseea9c972023-05-15 11:44:27 -0700146 // This code is a throw-free equivalent to
147 // beast::http::message::prepare_payload
148 boost::optional<uint64_t> pSize = stringResponse->payload_size();
149 using boost::beast::http::status;
150 using boost::beast::http::status_class;
151 using boost::beast::http::to_status_class;
152 if (!pSize)
153 {
154 pSize = 0;
155 }
156 else
157 {
158 bool is1XXReturn = to_status_class(stringResponse->result()) ==
159 status_class::informational;
160 if (*pSize > 0 &&
161 (is1XXReturn ||
162 stringResponse->result() == status::no_content ||
163 stringResponse->result() == status::not_modified))
164 {
165 BMCWEB_LOG_CRITICAL
166 << this
167 << " Response content provided but code was no-content or not_modified, which aren't allowed to have a body";
168 pSize = 0;
169 body().clear();
170 }
171 }
172 stringResponse->content_length(*pSize);
Ed Tanous23a21a12020-07-25 04:45:05 +0000173 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700174
175 void clear()
176 {
177 BMCWEB_LOG_DEBUG << this << " Clearing response containers";
178 stringResponse.emplace(response_type{});
Ed Tanousa6695a82023-05-16 11:39:18 -0700179 jsonValue = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700180 completed = false;
Ed Tanous291d7092022-04-13 12:34:57 -0700181 expectedHash = std::nullopt;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700182 }
183
Ed Tanous81ce6092020-12-17 16:54:55 +0000184 void write(std::string_view bodyPart)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700185 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000186 stringResponse->body() += std::string(bodyPart);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700187 }
188
Ed Tanous2d6cb562022-07-07 20:44:54 -0700189 std::string computeEtag() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700190 {
Ed Tanous89f18002022-03-24 18:38:24 -0700191 // Only set etag if this request succeeded
Ed Tanous2d6cb562022-07-07 20:44:54 -0700192 if (result() != boost::beast::http::status::ok)
Ed Tanous89f18002022-03-24 18:38:24 -0700193 {
Ed Tanous2d6cb562022-07-07 20:44:54 -0700194 return "";
195 }
196 // and the json response isn't empty
197 if (jsonValue.empty())
198 {
199 return "";
200 }
201 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
202 return "\"" + intToHexString(hashval, 8) + "\"";
203 }
204
205 void end()
206 {
207 std::string etag = computeEtag();
208 if (!etag.empty())
209 {
210 addHeader(boost::beast::http::field::etag, etag);
Ed Tanous89f18002022-03-24 18:38:24 -0700211 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700212 if (completed)
213 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800214 BMCWEB_LOG_ERROR << this << " Response was ended twice";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700215 return;
216 }
217 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800218 BMCWEB_LOG_DEBUG << this << " calling completion handler";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700219 if (completeRequestHandler)
220 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800221 BMCWEB_LOG_DEBUG << this << " completion handler was valid";
222 completeRequestHandler(*this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700223 }
224 }
225
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700226 bool isAlive() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700227 {
228 return isAliveHelper && isAliveHelper();
229 }
230
Nan Zhou72374eb2022-01-27 17:06:51 -0800231 void setCompleteRequestHandler(std::function<void(Response&)>&& handler)
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700232 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800233 BMCWEB_LOG_DEBUG << this << " setting completion handler";
234 completeRequestHandler = std::move(handler);
Ed Tanous13548d82022-07-22 09:50:44 -0700235
236 // Now that we have a new completion handler attached, we're no longer
237 // complete
238 completed = false;
Nan Zhou72374eb2022-01-27 17:06:51 -0800239 }
240
241 std::function<void(Response&)> releaseCompleteRequestHandler()
242 {
243 BMCWEB_LOG_DEBUG << this << " releasing completion handler"
244 << static_cast<bool>(completeRequestHandler);
245 std::function<void(Response&)> ret = completeRequestHandler;
246 completeRequestHandler = nullptr;
Ed Tanous13548d82022-07-22 09:50:44 -0700247 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800248 return ret;
249 }
250
251 void setIsAliveHelper(std::function<bool()>&& handler)
252 {
253 isAliveHelper = std::move(handler);
254 }
255
256 std::function<bool()> releaseIsAliveHelper()
257 {
258 std::function<bool()> ret = std::move(isAliveHelper);
259 isAliveHelper = nullptr;
260 return ret;
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700261 }
262
Ed Tanous291d7092022-04-13 12:34:57 -0700263 void setHashAndHandleNotModified()
264 {
265 // Can only hash if we have content that's valid
266 if (jsonValue.empty() || result() != boost::beast::http::status::ok)
267 {
268 return;
269 }
270 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
271 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\"";
272 addHeader(boost::beast::http::field::etag, hexVal);
273 if (expectedHash && hexVal == *expectedHash)
274 {
Ed Tanousa6695a82023-05-16 11:39:18 -0700275 jsonValue = nullptr;
Ed Tanous291d7092022-04-13 12:34:57 -0700276 result(boost::beast::http::status::not_modified);
277 }
278 }
279
280 void setExpectedHash(std::string_view hash)
281 {
282 expectedHash = hash;
283 }
284
Ed Tanous1abe55e2018-09-05 08:30:59 -0700285 private:
Ed Tanous291d7092022-04-13 12:34:57 -0700286 std::optional<std::string> expectedHash;
Nan Zhou72374eb2022-01-27 17:06:51 -0800287 bool completed = false;
288 std::function<void(Response&)> completeRequestHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700289 std::function<bool()> isAliveHelper;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700290};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700291} // namespace crow