blob: c411f8866ac425cc33aba1e0a5bcd82c386d4c55 [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 Tanous0242baf2024-05-16 19:52:47 -07006#include <fcntl.h>
7
Ed Tanousd43cd0c2020-09-30 20:46:53 -07008#include <boost/beast/http/message.hpp>
Ed Tanousfaf100f2023-05-25 10:03:14 -07009#include <nlohmann/json.hpp>
Ed Tanous7045c8d2017-04-03 10:04:37 -070010
Ed Tanous8a9a25c2021-05-11 14:50:58 -070011#include <optional>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050012#include <string>
Ed Tanous8a9a25c2021-05-11 14:50:58 -070013#include <string_view>
Abhilash Raju8e3f7032023-07-17 08:53:11 -050014#include <utility>
Ed Tanous0242baf2024-05-16 19:52:47 -070015
Ed Tanous1abe55e2018-09-05 08:30:59 -070016namespace crow
17{
Ed Tanouse0d918b2018-03-27 17:41:04 -070018
Ed Tanous52cc1122020-07-18 13:51:21 -070019template <typename Adaptor, typename Handler>
Ed Tanous7045c8d2017-04-03 10:04:37 -070020class Connection;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010021
Ed Tanous27b0cf92023-08-07 12:02:40 -070022namespace http = boost::beast::http;
23
Myung Baed51c61b2024-09-13 10:35:34 -050024enum class OpenCode
25{
26 Success,
27 FileDoesNotExist,
28 InternalError,
29};
30
Ed Tanous1abe55e2018-09-05 08:30:59 -070031struct Response
32{
Ed Tanous52cc1122020-07-18 13:51:21 -070033 template <typename Adaptor, typename Handler>
Ed Tanous1abe55e2018-09-05 08:30:59 -070034 friend class crow::Connection;
Ed Tanous7045c8d2017-04-03 10:04:37 -070035
Ed Tanousb2896142024-01-31 15:25:47 -080036 http::response<bmcweb::HttpBody> response;
Ed Tanouse0d918b2018-03-27 17:41:04 -070037
Ed Tanous1abe55e2018-09-05 08:30:59 -070038 nlohmann::json jsonValue;
Ed Tanous27b0cf92023-08-07 12:02:40 -070039 using fields_type = http::header<false, http::fields>;
40 fields_type& fields()
41 {
Ed Tanous52e31622024-01-23 16:31:11 -080042 return response.base();
Ed Tanous27b0cf92023-08-07 12:02:40 -070043 }
44
45 const fields_type& fields() const
46 {
Ed Tanous52e31622024-01-23 16:31:11 -080047 return response.base();
Ed Tanous27b0cf92023-08-07 12:02:40 -070048 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070049
Ed Tanous26ccae32023-02-16 10:28:44 -080050 void addHeader(std::string_view key, std::string_view value)
Ed Tanous1abe55e2018-09-05 08:30:59 -070051 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070052 fields().insert(key, value);
Ed Tanous7045c8d2017-04-03 10:04:37 -070053 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070054
Ed Tanous27b0cf92023-08-07 12:02:40 -070055 void addHeader(http::field key, std::string_view value)
Ed Tanous1abe55e2018-09-05 08:30:59 -070056 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070057 fields().insert(key, value);
Ed Tanous994fd862023-06-06 13:37:03 -070058 }
59
Ed Tanous27b0cf92023-08-07 12:02:40 -070060 void clearHeader(http::field key)
Ed Tanous994fd862023-06-06 13:37:03 -070061 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070062 fields().erase(key);
Ed Tanous2cd4cc12018-07-25 10:51:19 -070063 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070064
Ed Tanous52e31622024-01-23 16:31:11 -080065 Response() = default;
Ed Tanous13548d82022-07-22 09:50:44 -070066 Response(Response&& res) noexcept :
Ed Tanous27b0cf92023-08-07 12:02:40 -070067 response(std::move(res.response)), jsonValue(std::move(res.jsonValue)),
68 completed(res.completed)
Ed Tanous13548d82022-07-22 09:50:44 -070069 {
Ed Tanous13548d82022-07-22 09:50:44 -070070 // See note in operator= move handler for why this is needed.
71 if (!res.completed)
72 {
73 completeRequestHandler = std::move(res.completeRequestHandler);
74 res.completeRequestHandler = nullptr;
75 }
Ed Tanous13548d82022-07-22 09:50:44 -070076 }
77
Ed Tanousecd6a3a2022-01-07 09:18:40 -080078 ~Response() = default;
79
80 Response(const Response&) = delete;
Ed Tanous1abe55e2018-09-05 08:30:59 -070081 Response& operator=(const Response& r) = delete;
82
83 Response& operator=(Response&& r) noexcept
84 {
Ed Tanous62598e32023-07-17 17:06:25 -070085 BMCWEB_LOG_DEBUG("Moving response containers; this: {}; other: {}",
86 logPtr(this), logPtr(&r));
Nan Zhou72374eb2022-01-27 17:06:51 -080087 if (this == &r)
88 {
89 return *this;
90 }
Ed Tanous27b0cf92023-08-07 12:02:40 -070091 response = std::move(r.response);
Ed Tanous1abe55e2018-09-05 08:30:59 -070092 jsonValue = std::move(r.jsonValue);
Ed Tanous499b5b42024-04-06 08:39:18 -070093 expectedHash = std::move(r.expectedHash);
Ed Tanous13548d82022-07-22 09:50:44 -070094
95 // Only need to move completion handler if not already completed
96 // Note, there are cases where we might move out of a Response object
97 // while in a completion handler for that response object. This check
98 // is intended to prevent destructing the functor we are currently
99 // executing from in that case.
100 if (!r.completed)
101 {
102 completeRequestHandler = std::move(r.completeRequestHandler);
103 r.completeRequestHandler = nullptr;
104 }
105 else
106 {
107 completeRequestHandler = nullptr;
108 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 completed = r.completed;
110 return *this;
111 }
112
Nan Zhou3590bd12022-08-12 18:05:09 +0000113 void result(unsigned v)
114 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700115 fields().result(v);
Nan Zhou3590bd12022-08-12 18:05:09 +0000116 }
117
Ed Tanous27b0cf92023-08-07 12:02:40 -0700118 void result(http::status v)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700119 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700120 fields().result(v);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700121 }
122
Ed Tanous27b0cf92023-08-07 12:02:40 -0700123 void copyBody(const Response& res)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700124 {
Ed Tanous52e31622024-01-23 16:31:11 -0800125 response.body() = res.response.body();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700126 }
127
128 http::status result() const
129 {
130 return fields().result();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131 }
132
Carson Labrado039a47e2022-04-05 16:03:20 +0000133 unsigned resultInt() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700135 return fields().result_int();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700136 }
137
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700138 std::string_view reason() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700140 return fields().reason();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700141 }
142
143 bool isCompleted() const noexcept
144 {
145 return completed;
146 }
147
Ed Tanous27b0cf92023-08-07 12:02:40 -0700148 const std::string* body()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700149 {
Ed Tanous52e31622024-01-23 16:31:11 -0800150 return &response.body().str();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700151 }
152
Carson Labrado46a81462022-04-27 21:11:37 +0000153 std::string_view getHeaderValue(std::string_view key) const
154 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700155 return fields()[key];
Carson Labrado46a81462022-04-27 21:11:37 +0000156 }
157
Ed Tanous499b5b42024-04-06 08:39:18 -0700158 std::string_view getHeaderValue(boost::beast::http::field key) const
159 {
160 return fields()[key];
161 }
162
Ed Tanous1abe55e2018-09-05 08:30:59 -0700163 void keepAlive(bool k)
164 {
Ed Tanous52e31622024-01-23 16:31:11 -0800165 response.keep_alive(k);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700166 }
167
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700168 bool keepAlive() const
Ed Tanousceac6f72018-12-02 11:58:47 -0800169 {
Ed Tanous52e31622024-01-23 16:31:11 -0800170 return response.keep_alive();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700171 }
172
Ed Tanous52e31622024-01-23 16:31:11 -0800173 std::optional<uint64_t> size()
174 {
175 return response.body().payloadSize();
176 }
177
178 void preparePayload()
Ed Tanous27b0cf92023-08-07 12:02:40 -0700179 {
180 // This code is a throw-free equivalent to
181 // beast::http::message::prepare_payload
Ed Tanous52e31622024-01-23 16:31:11 -0800182 std::optional<uint64_t> pSize = response.body().payloadSize();
Ed Tanous0242baf2024-05-16 19:52:47 -0700183
Ed Tanous27b0cf92023-08-07 12:02:40 -0700184 using http::status;
185 using http::status_class;
186 using http::to_status_class;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700187 bool is1XXReturn = to_status_class(result()) ==
188 status_class::informational;
Ed Tanous0242baf2024-05-16 19:52:47 -0700189 if (!pSize)
190 {
191 response.chunked(true);
192 return;
193 }
194 response.content_length(*pSize);
195
196 if (is1XXReturn || result() == status::no_content ||
197 result() == status::not_modified)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700198 {
199 BMCWEB_LOG_CRITICAL("{} Response content provided but code was "
200 "no-content or not_modified, which aren't "
201 "allowed to have a body",
202 logPtr(this));
Ed Tanous52e31622024-01-23 16:31:11 -0800203 response.content_length(0);
204 return;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700205 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000206 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700207
208 void clear()
209 {
Ed Tanous62598e32023-07-17 17:06:25 -0700210 BMCWEB_LOG_DEBUG("{} Clearing response containers", logPtr(this));
Ed Tanous52e31622024-01-23 16:31:11 -0800211 response.clear();
Ed Tanous06fc9be2024-03-27 19:58:11 -0700212 response.body().clear();
Ed Tanous52e31622024-01-23 16:31:11 -0800213
Ed Tanousa6695a82023-05-16 11:39:18 -0700214 jsonValue = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700215 completed = false;
Ed Tanous291d7092022-04-13 12:34:57 -0700216 expectedHash = std::nullopt;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700217 }
218
Ed Tanous2d6cb562022-07-07 20:44:54 -0700219 std::string computeEtag() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700220 {
Ed Tanous89f18002022-03-24 18:38:24 -0700221 // Only set etag if this request succeeded
Ed Tanous27b0cf92023-08-07 12:02:40 -0700222 if (result() != http::status::ok)
Ed Tanous89f18002022-03-24 18:38:24 -0700223 {
Ed Tanous2d6cb562022-07-07 20:44:54 -0700224 return "";
225 }
226 // and the json response isn't empty
227 if (jsonValue.empty())
228 {
229 return "";
230 }
231 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
232 return "\"" + intToHexString(hashval, 8) + "\"";
233 }
234
Ed Tanous27b0cf92023-08-07 12:02:40 -0700235 void write(std::string&& bodyPart)
236 {
Ed Tanous52e31622024-01-23 16:31:11 -0800237 response.body().str() = std::move(bodyPart);
Ed Tanous27b0cf92023-08-07 12:02:40 -0700238 }
239
Ed Tanous2d6cb562022-07-07 20:44:54 -0700240 void end()
241 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700242 if (completed)
243 {
Ed Tanous62598e32023-07-17 17:06:25 -0700244 BMCWEB_LOG_ERROR("{} Response was ended twice", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700245 return;
246 }
247 completed = true;
Ed Tanous62598e32023-07-17 17:06:25 -0700248 BMCWEB_LOG_DEBUG("{} calling completion handler", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700249 if (completeRequestHandler)
250 {
Ed Tanous62598e32023-07-17 17:06:25 -0700251 BMCWEB_LOG_DEBUG("{} completion handler was valid", logPtr(this));
Nan Zhou72374eb2022-01-27 17:06:51 -0800252 completeRequestHandler(*this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700253 }
254 }
255
Nan Zhou72374eb2022-01-27 17:06:51 -0800256 void setCompleteRequestHandler(std::function<void(Response&)>&& handler)
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700257 {
Ed Tanous62598e32023-07-17 17:06:25 -0700258 BMCWEB_LOG_DEBUG("{} setting completion handler", logPtr(this));
Nan Zhou72374eb2022-01-27 17:06:51 -0800259 completeRequestHandler = std::move(handler);
Ed Tanous13548d82022-07-22 09:50:44 -0700260
261 // Now that we have a new completion handler attached, we're no longer
262 // complete
263 completed = false;
Nan Zhou72374eb2022-01-27 17:06:51 -0800264 }
265
266 std::function<void(Response&)> releaseCompleteRequestHandler()
267 {
Ed Tanous62598e32023-07-17 17:06:25 -0700268 BMCWEB_LOG_DEBUG("{} releasing completion handler{}", logPtr(this),
269 static_cast<bool>(completeRequestHandler));
Nan Zhou72374eb2022-01-27 17:06:51 -0800270 std::function<void(Response&)> ret = completeRequestHandler;
271 completeRequestHandler = nullptr;
Ed Tanous13548d82022-07-22 09:50:44 -0700272 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800273 return ret;
274 }
275
Ed Tanous291d7092022-04-13 12:34:57 -0700276 void setHashAndHandleNotModified()
277 {
278 // Can only hash if we have content that's valid
Ed Tanous27b0cf92023-08-07 12:02:40 -0700279 if (jsonValue.empty() || result() != http::status::ok)
Ed Tanous291d7092022-04-13 12:34:57 -0700280 {
281 return;
282 }
283 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
284 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\"";
Ed Tanous27b0cf92023-08-07 12:02:40 -0700285 addHeader(http::field::etag, hexVal);
Ed Tanous291d7092022-04-13 12:34:57 -0700286 if (expectedHash && hexVal == *expectedHash)
287 {
Ed Tanousa6695a82023-05-16 11:39:18 -0700288 jsonValue = nullptr;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700289 result(http::status::not_modified);
Ed Tanous291d7092022-04-13 12:34:57 -0700290 }
291 }
292
293 void setExpectedHash(std::string_view hash)
294 {
295 expectedHash = hash;
296 }
297
Myung Baed51c61b2024-09-13 10:35:34 -0500298 OpenCode openFile(const std::filesystem::path& path,
299 bmcweb::EncodingType enc = bmcweb::EncodingType::Raw)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700300 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700301 boost::beast::error_code ec;
Ed Tanous52e31622024-01-23 16:31:11 -0800302 response.body().open(path.c_str(), boost::beast::file_mode::read, ec);
303 response.body().encodingType = enc;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700304 if (ec)
305 {
Myung Baed51c61b2024-09-13 10:35:34 -0500306 BMCWEB_LOG_ERROR("Failed to open file {}, ec={}", path.c_str(),
307 ec.value());
308 if (ec.value() == boost::system::errc::no_such_file_or_directory)
309 {
310 return OpenCode::FileDoesNotExist;
311 }
312 return OpenCode::InternalError;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700313 }
Myung Baed51c61b2024-09-13 10:35:34 -0500314 return OpenCode::Success;
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600315 }
316
317 bool openFd(int fd, bmcweb::EncodingType enc = bmcweb::EncodingType::Raw)
318 {
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600319 boost::beast::error_code ec;
Ed Tanous0242baf2024-05-16 19:52:47 -0700320 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
321 int retval = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
322 if (retval == -1)
323 {
324 BMCWEB_LOG_ERROR("Setting O_NONBLOCK failed");
325 }
Ed Tanous52e31622024-01-23 16:31:11 -0800326 response.body().encodingType = enc;
327 response.body().setFd(fd, ec);
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600328 if (ec)
329 {
330 BMCWEB_LOG_ERROR("Failed to set fd");
331 return false;
332 }
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600333 return true;
334 }
335
336 private:
Ed Tanous291d7092022-04-13 12:34:57 -0700337 std::optional<std::string> expectedHash;
Nan Zhou72374eb2022-01-27 17:06:51 -0800338 bool completed = false;
339 std::function<void(Response&)> completeRequestHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700340};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700341} // namespace crow