blob: 2af12c9d05026f24997435ab78be27862a3bc23b [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 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 {
146 stringResponse->prepare_payload();
Ed Tanous23a21a12020-07-25 04:45:05 +0000147 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700148
149 void clear()
150 {
151 BMCWEB_LOG_DEBUG << this << " Clearing response containers";
152 stringResponse.emplace(response_type{});
153 jsonValue.clear();
154 completed = false;
Ed Tanous291d7092022-04-13 12:34:57 -0700155 expectedHash = std::nullopt;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700156 }
157
Ed Tanous81ce6092020-12-17 16:54:55 +0000158 void write(std::string_view bodyPart)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 {
Ed Tanous81ce6092020-12-17 16:54:55 +0000160 stringResponse->body() += std::string(bodyPart);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700161 }
162
Ed Tanous2d6cb562022-07-07 20:44:54 -0700163 std::string computeEtag() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700164 {
Ed Tanous89f18002022-03-24 18:38:24 -0700165 // Only set etag if this request succeeded
Ed Tanous2d6cb562022-07-07 20:44:54 -0700166 if (result() != boost::beast::http::status::ok)
Ed Tanous89f18002022-03-24 18:38:24 -0700167 {
Ed Tanous2d6cb562022-07-07 20:44:54 -0700168 return "";
169 }
170 // and the json response isn't empty
171 if (jsonValue.empty())
172 {
173 return "";
174 }
175 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
176 return "\"" + intToHexString(hashval, 8) + "\"";
177 }
178
179 void end()
180 {
181 std::string etag = computeEtag();
182 if (!etag.empty())
183 {
184 addHeader(boost::beast::http::field::etag, etag);
Ed Tanous89f18002022-03-24 18:38:24 -0700185 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700186 if (completed)
187 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800188 BMCWEB_LOG_ERROR << this << " Response was ended twice";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700189 return;
190 }
191 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800192 BMCWEB_LOG_DEBUG << this << " calling completion handler";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700193 if (completeRequestHandler)
194 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800195 BMCWEB_LOG_DEBUG << this << " completion handler was valid";
196 completeRequestHandler(*this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700197 }
198 }
199
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700200 bool isAlive() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700201 {
202 return isAliveHelper && isAliveHelper();
203 }
204
Nan Zhou72374eb2022-01-27 17:06:51 -0800205 void setCompleteRequestHandler(std::function<void(Response&)>&& handler)
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700206 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800207 BMCWEB_LOG_DEBUG << this << " setting completion handler";
208 completeRequestHandler = std::move(handler);
Ed Tanous13548d82022-07-22 09:50:44 -0700209
210 // Now that we have a new completion handler attached, we're no longer
211 // complete
212 completed = false;
Nan Zhou72374eb2022-01-27 17:06:51 -0800213 }
214
215 std::function<void(Response&)> releaseCompleteRequestHandler()
216 {
217 BMCWEB_LOG_DEBUG << this << " releasing completion handler"
218 << static_cast<bool>(completeRequestHandler);
219 std::function<void(Response&)> ret = completeRequestHandler;
220 completeRequestHandler = nullptr;
Ed Tanous13548d82022-07-22 09:50:44 -0700221 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800222 return ret;
223 }
224
225 void setIsAliveHelper(std::function<bool()>&& handler)
226 {
227 isAliveHelper = std::move(handler);
228 }
229
230 std::function<bool()> releaseIsAliveHelper()
231 {
232 std::function<bool()> ret = std::move(isAliveHelper);
233 isAliveHelper = nullptr;
234 return ret;
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700235 }
236
Ed Tanous291d7092022-04-13 12:34:57 -0700237 void setHashAndHandleNotModified()
238 {
239 // Can only hash if we have content that's valid
240 if (jsonValue.empty() || result() != boost::beast::http::status::ok)
241 {
242 return;
243 }
244 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
245 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\"";
246 addHeader(boost::beast::http::field::etag, hexVal);
247 if (expectedHash && hexVal == *expectedHash)
248 {
249 jsonValue.clear();
250 result(boost::beast::http::status::not_modified);
251 }
252 }
253
254 void setExpectedHash(std::string_view hash)
255 {
256 expectedHash = hash;
257 }
258
Ed Tanous1abe55e2018-09-05 08:30:59 -0700259 private:
Ed Tanous291d7092022-04-13 12:34:57 -0700260 std::optional<std::string> expectedHash;
Nan Zhou72374eb2022-01-27 17:06:51 -0800261 bool completed = false;
262 std::function<void(Response&)> completeRequestHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700263 std::function<bool()> isAliveHelper;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700264};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700265} // namespace crow