blob: 213117dbd6470952d4fb76fe7ca6949aa143c20a [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"
4
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 Tanous89f18002022-03-24 18:38:24 -07007#include <utils/hex_utils.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 :
44 stringResponse(std::move(res.stringResponse)), completed(res.completed)
45 {
46 jsonValue = std::move(res.jsonValue);
47 // 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
164 void end()
165 {
Ed Tanous89f18002022-03-24 18:38:24 -0700166 // Only set etag if this request succeeded
167 if (result() == boost::beast::http::status::ok)
168 {
169 // and the json response isn't empty
170 if (!jsonValue.empty())
171 {
172 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
173 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\"";
174 addHeader(boost::beast::http::field::etag, hexVal);
175 }
176 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700177 if (completed)
178 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800179 BMCWEB_LOG_ERROR << this << " Response was ended twice";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700180 return;
181 }
182 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800183 BMCWEB_LOG_DEBUG << this << " calling completion handler";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700184 if (completeRequestHandler)
185 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800186 BMCWEB_LOG_DEBUG << this << " completion handler was valid";
187 completeRequestHandler(*this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700188 }
189 }
190
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700191 bool isAlive() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700192 {
193 return isAliveHelper && isAliveHelper();
194 }
195
Nan Zhou72374eb2022-01-27 17:06:51 -0800196 void setCompleteRequestHandler(std::function<void(Response&)>&& handler)
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700197 {
Nan Zhou72374eb2022-01-27 17:06:51 -0800198 BMCWEB_LOG_DEBUG << this << " setting completion handler";
199 completeRequestHandler = std::move(handler);
Ed Tanous13548d82022-07-22 09:50:44 -0700200
201 // Now that we have a new completion handler attached, we're no longer
202 // complete
203 completed = false;
Nan Zhou72374eb2022-01-27 17:06:51 -0800204 }
205
206 std::function<void(Response&)> releaseCompleteRequestHandler()
207 {
208 BMCWEB_LOG_DEBUG << this << " releasing completion handler"
209 << static_cast<bool>(completeRequestHandler);
210 std::function<void(Response&)> ret = completeRequestHandler;
211 completeRequestHandler = nullptr;
Ed Tanous13548d82022-07-22 09:50:44 -0700212 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800213 return ret;
214 }
215
216 void setIsAliveHelper(std::function<bool()>&& handler)
217 {
218 isAliveHelper = std::move(handler);
219 }
220
221 std::function<bool()> releaseIsAliveHelper()
222 {
223 std::function<bool()> ret = std::move(isAliveHelper);
224 isAliveHelper = nullptr;
225 return ret;
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700226 }
227
Ed Tanous291d7092022-04-13 12:34:57 -0700228 void setHashAndHandleNotModified()
229 {
230 // Can only hash if we have content that's valid
231 if (jsonValue.empty() || result() != boost::beast::http::status::ok)
232 {
233 return;
234 }
235 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
236 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\"";
237 addHeader(boost::beast::http::field::etag, hexVal);
238 if (expectedHash && hexVal == *expectedHash)
239 {
240 jsonValue.clear();
241 result(boost::beast::http::status::not_modified);
242 }
243 }
244
245 void setExpectedHash(std::string_view hash)
246 {
247 expectedHash = hash;
248 }
249
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250 private:
Ed Tanous291d7092022-04-13 12:34:57 -0700251 std::optional<std::string> expectedHash;
Nan Zhou72374eb2022-01-27 17:06:51 -0800252 bool completed = false;
253 std::function<void(Response&)> completeRequestHandler;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700254 std::function<bool()> isAliveHelper;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700255};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700256} // namespace crow