blob: 18266ec93d3ed400c6bde09c96af87008d21bef3 [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
Ed Tanousb2896142024-01-31 15:25:47 -08002#include "http_body.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -07003#include "logging.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 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>
Abhilash Raju8e3f7032023-07-17 08:53:11 -050012#include <utility>
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 Tanous27b0cf92023-08-07 12:02:40 -070019namespace http = boost::beast::http;
20
Ed Tanous1abe55e2018-09-05 08:30:59 -070021struct Response
22{
Ed Tanous52cc1122020-07-18 13:51:21 -070023 template <typename Adaptor, typename Handler>
Ed Tanous1abe55e2018-09-05 08:30:59 -070024 friend class crow::Connection;
Ed Tanous7045c8d2017-04-03 10:04:37 -070025
Ed Tanousb2896142024-01-31 15:25:47 -080026 http::response<bmcweb::HttpBody> response;
Ed Tanouse0d918b2018-03-27 17:41:04 -070027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028 nlohmann::json jsonValue;
Ed Tanous27b0cf92023-08-07 12:02:40 -070029 using fields_type = http::header<false, http::fields>;
30 fields_type& fields()
31 {
Ed Tanous52e31622024-01-23 16:31:11 -080032 return response.base();
Ed Tanous27b0cf92023-08-07 12:02:40 -070033 }
34
35 const fields_type& fields() const
36 {
Ed Tanous52e31622024-01-23 16:31:11 -080037 return response.base();
Ed Tanous27b0cf92023-08-07 12:02:40 -070038 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070039
Ed Tanous26ccae32023-02-16 10:28:44 -080040 void addHeader(std::string_view key, std::string_view value)
Ed Tanous1abe55e2018-09-05 08:30:59 -070041 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070042 fields().insert(key, value);
Ed Tanous7045c8d2017-04-03 10:04:37 -070043 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070044
Ed Tanous27b0cf92023-08-07 12:02:40 -070045 void addHeader(http::field key, std::string_view value)
Ed Tanous1abe55e2018-09-05 08:30:59 -070046 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070047 fields().insert(key, value);
Ed Tanous994fd862023-06-06 13:37:03 -070048 }
49
Ed Tanous27b0cf92023-08-07 12:02:40 -070050 void clearHeader(http::field key)
Ed Tanous994fd862023-06-06 13:37:03 -070051 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070052 fields().erase(key);
Ed Tanous2cd4cc12018-07-25 10:51:19 -070053 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070054
Ed Tanous52e31622024-01-23 16:31:11 -080055 Response() = default;
Ed Tanous13548d82022-07-22 09:50:44 -070056 Response(Response&& res) noexcept :
Ed Tanous27b0cf92023-08-07 12:02:40 -070057 response(std::move(res.response)), jsonValue(std::move(res.jsonValue)),
58 completed(res.completed)
Ed Tanous13548d82022-07-22 09:50:44 -070059 {
Ed Tanous13548d82022-07-22 09:50:44 -070060 // See note in operator= move handler for why this is needed.
61 if (!res.completed)
62 {
63 completeRequestHandler = std::move(res.completeRequestHandler);
64 res.completeRequestHandler = nullptr;
65 }
Ed Tanous13548d82022-07-22 09:50:44 -070066 }
67
Ed Tanousecd6a3a2022-01-07 09:18:40 -080068 ~Response() = default;
69
70 Response(const Response&) = delete;
Ed Tanous1abe55e2018-09-05 08:30:59 -070071 Response& operator=(const Response& r) = delete;
72
73 Response& operator=(Response&& r) noexcept
74 {
Ed Tanous62598e32023-07-17 17:06:25 -070075 BMCWEB_LOG_DEBUG("Moving response containers; this: {}; other: {}",
76 logPtr(this), logPtr(&r));
Nan Zhou72374eb2022-01-27 17:06:51 -080077 if (this == &r)
78 {
79 return *this;
80 }
Ed Tanous27b0cf92023-08-07 12:02:40 -070081 response = std::move(r.response);
Ed Tanous1abe55e2018-09-05 08:30:59 -070082 jsonValue = std::move(r.jsonValue);
Ed Tanous499b5b42024-04-06 08:39:18 -070083 expectedHash = std::move(r.expectedHash);
Ed Tanous13548d82022-07-22 09:50:44 -070084
85 // Only need to move completion handler if not already completed
86 // Note, there are cases where we might move out of a Response object
87 // while in a completion handler for that response object. This check
88 // is intended to prevent destructing the functor we are currently
89 // executing from in that case.
90 if (!r.completed)
91 {
92 completeRequestHandler = std::move(r.completeRequestHandler);
93 r.completeRequestHandler = nullptr;
94 }
95 else
96 {
97 completeRequestHandler = nullptr;
98 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070099 completed = r.completed;
100 return *this;
101 }
102
Nan Zhou3590bd12022-08-12 18:05:09 +0000103 void result(unsigned v)
104 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700105 fields().result(v);
Nan Zhou3590bd12022-08-12 18:05:09 +0000106 }
107
Ed Tanous27b0cf92023-08-07 12:02:40 -0700108 void result(http::status v)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700110 fields().result(v);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111 }
112
Ed Tanous27b0cf92023-08-07 12:02:40 -0700113 void copyBody(const Response& res)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 {
Ed Tanous52e31622024-01-23 16:31:11 -0800115 response.body() = res.response.body();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700116 }
117
118 http::status result() const
119 {
120 return fields().result();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121 }
122
Carson Labrado039a47e2022-04-05 16:03:20 +0000123 unsigned resultInt() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700124 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700125 return fields().result_int();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700126 }
127
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700128 std::string_view reason() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700129 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700130 return fields().reason();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131 }
132
133 bool isCompleted() const noexcept
134 {
135 return completed;
136 }
137
Ed Tanous27b0cf92023-08-07 12:02:40 -0700138 const std::string* body()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 {
Ed Tanous52e31622024-01-23 16:31:11 -0800140 return &response.body().str();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700141 }
142
Carson Labrado46a81462022-04-27 21:11:37 +0000143 std::string_view getHeaderValue(std::string_view key) const
144 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700145 return fields()[key];
Carson Labrado46a81462022-04-27 21:11:37 +0000146 }
147
Ed Tanous499b5b42024-04-06 08:39:18 -0700148 std::string_view getHeaderValue(boost::beast::http::field key) const
149 {
150 return fields()[key];
151 }
152
Ed Tanous1abe55e2018-09-05 08:30:59 -0700153 void keepAlive(bool k)
154 {
Ed Tanous52e31622024-01-23 16:31:11 -0800155 response.keep_alive(k);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700156 }
157
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700158 bool keepAlive() const
Ed Tanousceac6f72018-12-02 11:58:47 -0800159 {
Ed Tanous52e31622024-01-23 16:31:11 -0800160 return response.keep_alive();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700161 }
162
Ed Tanous52e31622024-01-23 16:31:11 -0800163 std::optional<uint64_t> size()
164 {
165 return response.body().payloadSize();
166 }
167
168 void preparePayload()
Ed Tanous27b0cf92023-08-07 12:02:40 -0700169 {
170 // This code is a throw-free equivalent to
171 // beast::http::message::prepare_payload
Ed Tanous52e31622024-01-23 16:31:11 -0800172 std::optional<uint64_t> pSize = response.body().payloadSize();
173 if (!pSize)
174 {
175 return;
176 }
Ed Tanous27b0cf92023-08-07 12:02:40 -0700177 using http::status;
178 using http::status_class;
179 using http::to_status_class;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700180 bool is1XXReturn = to_status_class(result()) ==
181 status_class::informational;
182 if (*pSize > 0 && (is1XXReturn || result() == status::no_content ||
183 result() == status::not_modified))
184 {
185 BMCWEB_LOG_CRITICAL("{} Response content provided but code was "
186 "no-content or not_modified, which aren't "
187 "allowed to have a body",
188 logPtr(this));
Ed Tanous52e31622024-01-23 16:31:11 -0800189 response.content_length(0);
190 return;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700191 }
Ed Tanous52e31622024-01-23 16:31:11 -0800192 response.content_length(*pSize);
Ed Tanous23a21a12020-07-25 04:45:05 +0000193 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700194
195 void clear()
196 {
Ed Tanous62598e32023-07-17 17:06:25 -0700197 BMCWEB_LOG_DEBUG("{} Clearing response containers", logPtr(this));
Ed Tanous52e31622024-01-23 16:31:11 -0800198 response.clear();
Ed Tanous06fc9be2024-03-27 19:58:11 -0700199 response.body().clear();
Ed Tanous52e31622024-01-23 16:31:11 -0800200
Ed Tanousa6695a82023-05-16 11:39:18 -0700201 jsonValue = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700202 completed = false;
Ed Tanous291d7092022-04-13 12:34:57 -0700203 expectedHash = std::nullopt;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700204 }
205
Ed Tanous2d6cb562022-07-07 20:44:54 -0700206 std::string computeEtag() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700207 {
Ed Tanous89f18002022-03-24 18:38:24 -0700208 // Only set etag if this request succeeded
Ed Tanous27b0cf92023-08-07 12:02:40 -0700209 if (result() != http::status::ok)
Ed Tanous89f18002022-03-24 18:38:24 -0700210 {
Ed Tanous2d6cb562022-07-07 20:44:54 -0700211 return "";
212 }
213 // and the json response isn't empty
214 if (jsonValue.empty())
215 {
216 return "";
217 }
218 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
219 return "\"" + intToHexString(hashval, 8) + "\"";
220 }
221
Ed Tanous27b0cf92023-08-07 12:02:40 -0700222 void write(std::string&& bodyPart)
223 {
Ed Tanous52e31622024-01-23 16:31:11 -0800224 response.body().str() = std::move(bodyPart);
Ed Tanous27b0cf92023-08-07 12:02:40 -0700225 }
226
Ed Tanous2d6cb562022-07-07 20:44:54 -0700227 void end()
228 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700229 if (completed)
230 {
Ed Tanous62598e32023-07-17 17:06:25 -0700231 BMCWEB_LOG_ERROR("{} Response was ended twice", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700232 return;
233 }
234 completed = true;
Ed Tanous62598e32023-07-17 17:06:25 -0700235 BMCWEB_LOG_DEBUG("{} calling completion handler", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700236 if (completeRequestHandler)
237 {
Ed Tanous62598e32023-07-17 17:06:25 -0700238 BMCWEB_LOG_DEBUG("{} completion handler was valid", logPtr(this));
Nan Zhou72374eb2022-01-27 17:06:51 -0800239 completeRequestHandler(*this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700240 }
241 }
242
Nan Zhou72374eb2022-01-27 17:06:51 -0800243 void setCompleteRequestHandler(std::function<void(Response&)>&& handler)
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700244 {
Ed Tanous62598e32023-07-17 17:06:25 -0700245 BMCWEB_LOG_DEBUG("{} setting completion handler", logPtr(this));
Nan Zhou72374eb2022-01-27 17:06:51 -0800246 completeRequestHandler = std::move(handler);
Ed Tanous13548d82022-07-22 09:50:44 -0700247
248 // Now that we have a new completion handler attached, we're no longer
249 // complete
250 completed = false;
Nan Zhou72374eb2022-01-27 17:06:51 -0800251 }
252
253 std::function<void(Response&)> releaseCompleteRequestHandler()
254 {
Ed Tanous62598e32023-07-17 17:06:25 -0700255 BMCWEB_LOG_DEBUG("{} releasing completion handler{}", logPtr(this),
256 static_cast<bool>(completeRequestHandler));
Nan Zhou72374eb2022-01-27 17:06:51 -0800257 std::function<void(Response&)> ret = completeRequestHandler;
258 completeRequestHandler = nullptr;
Ed Tanous13548d82022-07-22 09:50:44 -0700259 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800260 return ret;
261 }
262
Ed Tanous291d7092022-04-13 12:34:57 -0700263 void setHashAndHandleNotModified()
264 {
265 // Can only hash if we have content that's valid
Ed Tanous27b0cf92023-08-07 12:02:40 -0700266 if (jsonValue.empty() || result() != http::status::ok)
Ed Tanous291d7092022-04-13 12:34:57 -0700267 {
268 return;
269 }
270 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
271 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\"";
Ed Tanous27b0cf92023-08-07 12:02:40 -0700272 addHeader(http::field::etag, hexVal);
Ed Tanous291d7092022-04-13 12:34:57 -0700273 if (expectedHash && hexVal == *expectedHash)
274 {
Ed Tanousa6695a82023-05-16 11:39:18 -0700275 jsonValue = nullptr;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700276 result(http::status::not_modified);
Ed Tanous291d7092022-04-13 12:34:57 -0700277 }
278 }
279
280 void setExpectedHash(std::string_view hash)
281 {
282 expectedHash = hash;
283 }
284
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600285 bool openFile(const std::filesystem::path& path,
286 bmcweb::EncodingType enc = bmcweb::EncodingType::Raw)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700287 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700288 boost::beast::error_code ec;
Ed Tanous52e31622024-01-23 16:31:11 -0800289 response.body().open(path.c_str(), boost::beast::file_mode::read, ec);
290 response.body().encodingType = enc;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700291 if (ec)
292 {
Ed Tanous52e31622024-01-23 16:31:11 -0800293 BMCWEB_LOG_ERROR("Failed to open file {}", path.c_str());
Ed Tanous27b0cf92023-08-07 12:02:40 -0700294 return false;
295 }
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600296 return true;
297 }
298
299 bool openFd(int fd, bmcweb::EncodingType enc = bmcweb::EncodingType::Raw)
300 {
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600301 boost::beast::error_code ec;
Ed Tanous52e31622024-01-23 16:31:11 -0800302 response.body().encodingType = enc;
303 response.body().setFd(fd, ec);
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600304 if (ec)
305 {
306 BMCWEB_LOG_ERROR("Failed to set fd");
307 return false;
308 }
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600309 return true;
310 }
311
312 private:
Ed Tanous291d7092022-04-13 12:34:57 -0700313 std::optional<std::string> expectedHash;
Nan Zhou72374eb2022-01-27 17:06:51 -0800314 bool completed = false;
315 std::function<void(Response&)> completeRequestHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700316};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317} // namespace crow