blob: a4c8375c824a3e99d251934c4492917f53292995 [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
Ed Tanous1abe55e2018-09-05 08:30:59 -070024struct Response
25{
Ed Tanous52cc1122020-07-18 13:51:21 -070026 template <typename Adaptor, typename Handler>
Ed Tanous1abe55e2018-09-05 08:30:59 -070027 friend class crow::Connection;
Ed Tanous7045c8d2017-04-03 10:04:37 -070028
Ed Tanousb2896142024-01-31 15:25:47 -080029 http::response<bmcweb::HttpBody> response;
Ed Tanouse0d918b2018-03-27 17:41:04 -070030
Ed Tanous1abe55e2018-09-05 08:30:59 -070031 nlohmann::json jsonValue;
Ed Tanous27b0cf92023-08-07 12:02:40 -070032 using fields_type = http::header<false, http::fields>;
33 fields_type& fields()
34 {
Ed Tanous52e31622024-01-23 16:31:11 -080035 return response.base();
Ed Tanous27b0cf92023-08-07 12:02:40 -070036 }
37
38 const fields_type& fields() const
39 {
Ed Tanous52e31622024-01-23 16:31:11 -080040 return response.base();
Ed Tanous27b0cf92023-08-07 12:02:40 -070041 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070042
Ed Tanous26ccae32023-02-16 10:28:44 -080043 void addHeader(std::string_view key, std::string_view value)
Ed Tanous1abe55e2018-09-05 08:30:59 -070044 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070045 fields().insert(key, value);
Ed Tanous7045c8d2017-04-03 10:04:37 -070046 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070047
Ed Tanous27b0cf92023-08-07 12:02:40 -070048 void addHeader(http::field key, std::string_view value)
Ed Tanous1abe55e2018-09-05 08:30:59 -070049 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070050 fields().insert(key, value);
Ed Tanous994fd862023-06-06 13:37:03 -070051 }
52
Ed Tanous27b0cf92023-08-07 12:02:40 -070053 void clearHeader(http::field key)
Ed Tanous994fd862023-06-06 13:37:03 -070054 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070055 fields().erase(key);
Ed Tanous2cd4cc12018-07-25 10:51:19 -070056 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070057
Ed Tanous52e31622024-01-23 16:31:11 -080058 Response() = default;
Ed Tanous13548d82022-07-22 09:50:44 -070059 Response(Response&& res) noexcept :
Ed Tanous27b0cf92023-08-07 12:02:40 -070060 response(std::move(res.response)), jsonValue(std::move(res.jsonValue)),
61 completed(res.completed)
Ed Tanous13548d82022-07-22 09:50:44 -070062 {
Ed Tanous13548d82022-07-22 09:50:44 -070063 // See note in operator= move handler for why this is needed.
64 if (!res.completed)
65 {
66 completeRequestHandler = std::move(res.completeRequestHandler);
67 res.completeRequestHandler = nullptr;
68 }
Ed Tanous13548d82022-07-22 09:50:44 -070069 }
70
Ed Tanousecd6a3a2022-01-07 09:18:40 -080071 ~Response() = default;
72
73 Response(const Response&) = delete;
Ed Tanous1abe55e2018-09-05 08:30:59 -070074 Response& operator=(const Response& r) = delete;
75
76 Response& operator=(Response&& r) noexcept
77 {
Ed Tanous62598e32023-07-17 17:06:25 -070078 BMCWEB_LOG_DEBUG("Moving response containers; this: {}; other: {}",
79 logPtr(this), logPtr(&r));
Nan Zhou72374eb2022-01-27 17:06:51 -080080 if (this == &r)
81 {
82 return *this;
83 }
Ed Tanous27b0cf92023-08-07 12:02:40 -070084 response = std::move(r.response);
Ed Tanous1abe55e2018-09-05 08:30:59 -070085 jsonValue = std::move(r.jsonValue);
Ed Tanous499b5b42024-04-06 08:39:18 -070086 expectedHash = std::move(r.expectedHash);
Ed Tanous13548d82022-07-22 09:50:44 -070087
88 // Only need to move completion handler if not already completed
89 // Note, there are cases where we might move out of a Response object
90 // while in a completion handler for that response object. This check
91 // is intended to prevent destructing the functor we are currently
92 // executing from in that case.
93 if (!r.completed)
94 {
95 completeRequestHandler = std::move(r.completeRequestHandler);
96 r.completeRequestHandler = nullptr;
97 }
98 else
99 {
100 completeRequestHandler = nullptr;
101 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700102 completed = r.completed;
103 return *this;
104 }
105
Nan Zhou3590bd12022-08-12 18:05:09 +0000106 void result(unsigned v)
107 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700108 fields().result(v);
Nan Zhou3590bd12022-08-12 18:05:09 +0000109 }
110
Ed Tanous27b0cf92023-08-07 12:02:40 -0700111 void result(http::status v)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700113 fields().result(v);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114 }
115
Ed Tanous27b0cf92023-08-07 12:02:40 -0700116 void copyBody(const Response& res)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700117 {
Ed Tanous52e31622024-01-23 16:31:11 -0800118 response.body() = res.response.body();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700119 }
120
121 http::status result() const
122 {
123 return fields().result();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700124 }
125
Carson Labrado039a47e2022-04-05 16:03:20 +0000126 unsigned resultInt() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700127 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700128 return fields().result_int();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700129 }
130
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700131 std::string_view reason() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700132 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700133 return fields().reason();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134 }
135
136 bool isCompleted() const noexcept
137 {
138 return completed;
139 }
140
Ed Tanous27b0cf92023-08-07 12:02:40 -0700141 const std::string* body()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700142 {
Ed Tanous52e31622024-01-23 16:31:11 -0800143 return &response.body().str();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700144 }
145
Carson Labrado46a81462022-04-27 21:11:37 +0000146 std::string_view getHeaderValue(std::string_view key) const
147 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700148 return fields()[key];
Carson Labrado46a81462022-04-27 21:11:37 +0000149 }
150
Ed Tanous499b5b42024-04-06 08:39:18 -0700151 std::string_view getHeaderValue(boost::beast::http::field key) const
152 {
153 return fields()[key];
154 }
155
Ed Tanous1abe55e2018-09-05 08:30:59 -0700156 void keepAlive(bool k)
157 {
Ed Tanous52e31622024-01-23 16:31:11 -0800158 response.keep_alive(k);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 }
160
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700161 bool keepAlive() const
Ed Tanousceac6f72018-12-02 11:58:47 -0800162 {
Ed Tanous52e31622024-01-23 16:31:11 -0800163 return response.keep_alive();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700164 }
165
Ed Tanous52e31622024-01-23 16:31:11 -0800166 std::optional<uint64_t> size()
167 {
168 return response.body().payloadSize();
169 }
170
171 void preparePayload()
Ed Tanous27b0cf92023-08-07 12:02:40 -0700172 {
173 // This code is a throw-free equivalent to
174 // beast::http::message::prepare_payload
Ed Tanous52e31622024-01-23 16:31:11 -0800175 std::optional<uint64_t> pSize = response.body().payloadSize();
Ed Tanous0242baf2024-05-16 19:52:47 -0700176
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;
Ed Tanous0242baf2024-05-16 19:52:47 -0700182 if (!pSize)
183 {
184 response.chunked(true);
185 return;
186 }
187 response.content_length(*pSize);
188
189 if (is1XXReturn || result() == status::no_content ||
190 result() == status::not_modified)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700191 {
192 BMCWEB_LOG_CRITICAL("{} Response content provided but code was "
193 "no-content or not_modified, which aren't "
194 "allowed to have a body",
195 logPtr(this));
Ed Tanous52e31622024-01-23 16:31:11 -0800196 response.content_length(0);
197 return;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700198 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000199 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700200
201 void clear()
202 {
Ed Tanous62598e32023-07-17 17:06:25 -0700203 BMCWEB_LOG_DEBUG("{} Clearing response containers", logPtr(this));
Ed Tanous52e31622024-01-23 16:31:11 -0800204 response.clear();
Ed Tanous06fc9be2024-03-27 19:58:11 -0700205 response.body().clear();
Ed Tanous52e31622024-01-23 16:31:11 -0800206
Ed Tanousa6695a82023-05-16 11:39:18 -0700207 jsonValue = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700208 completed = false;
Ed Tanous291d7092022-04-13 12:34:57 -0700209 expectedHash = std::nullopt;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700210 }
211
Ed Tanous2d6cb562022-07-07 20:44:54 -0700212 std::string computeEtag() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700213 {
Ed Tanous89f18002022-03-24 18:38:24 -0700214 // Only set etag if this request succeeded
Ed Tanous27b0cf92023-08-07 12:02:40 -0700215 if (result() != http::status::ok)
Ed Tanous89f18002022-03-24 18:38:24 -0700216 {
Ed Tanous2d6cb562022-07-07 20:44:54 -0700217 return "";
218 }
219 // and the json response isn't empty
220 if (jsonValue.empty())
221 {
222 return "";
223 }
224 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
225 return "\"" + intToHexString(hashval, 8) + "\"";
226 }
227
Ed Tanous27b0cf92023-08-07 12:02:40 -0700228 void write(std::string&& bodyPart)
229 {
Ed Tanous52e31622024-01-23 16:31:11 -0800230 response.body().str() = std::move(bodyPart);
Ed Tanous27b0cf92023-08-07 12:02:40 -0700231 }
232
Ed Tanous2d6cb562022-07-07 20:44:54 -0700233 void end()
234 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700235 if (completed)
236 {
Ed Tanous62598e32023-07-17 17:06:25 -0700237 BMCWEB_LOG_ERROR("{} Response was ended twice", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700238 return;
239 }
240 completed = true;
Ed Tanous62598e32023-07-17 17:06:25 -0700241 BMCWEB_LOG_DEBUG("{} calling completion handler", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700242 if (completeRequestHandler)
243 {
Ed Tanous62598e32023-07-17 17:06:25 -0700244 BMCWEB_LOG_DEBUG("{} completion handler was valid", logPtr(this));
Nan Zhou72374eb2022-01-27 17:06:51 -0800245 completeRequestHandler(*this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700246 }
247 }
248
Nan Zhou72374eb2022-01-27 17:06:51 -0800249 void setCompleteRequestHandler(std::function<void(Response&)>&& handler)
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700250 {
Ed Tanous62598e32023-07-17 17:06:25 -0700251 BMCWEB_LOG_DEBUG("{} setting completion handler", logPtr(this));
Nan Zhou72374eb2022-01-27 17:06:51 -0800252 completeRequestHandler = std::move(handler);
Ed Tanous13548d82022-07-22 09:50:44 -0700253
254 // Now that we have a new completion handler attached, we're no longer
255 // complete
256 completed = false;
Nan Zhou72374eb2022-01-27 17:06:51 -0800257 }
258
259 std::function<void(Response&)> releaseCompleteRequestHandler()
260 {
Ed Tanous62598e32023-07-17 17:06:25 -0700261 BMCWEB_LOG_DEBUG("{} releasing completion handler{}", logPtr(this),
262 static_cast<bool>(completeRequestHandler));
Nan Zhou72374eb2022-01-27 17:06:51 -0800263 std::function<void(Response&)> ret = completeRequestHandler;
264 completeRequestHandler = nullptr;
Ed Tanous13548d82022-07-22 09:50:44 -0700265 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800266 return ret;
267 }
268
Ed Tanous291d7092022-04-13 12:34:57 -0700269 void setHashAndHandleNotModified()
270 {
271 // Can only hash if we have content that's valid
Ed Tanous27b0cf92023-08-07 12:02:40 -0700272 if (jsonValue.empty() || result() != http::status::ok)
Ed Tanous291d7092022-04-13 12:34:57 -0700273 {
274 return;
275 }
276 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
277 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\"";
Ed Tanous27b0cf92023-08-07 12:02:40 -0700278 addHeader(http::field::etag, hexVal);
Ed Tanous291d7092022-04-13 12:34:57 -0700279 if (expectedHash && hexVal == *expectedHash)
280 {
Ed Tanousa6695a82023-05-16 11:39:18 -0700281 jsonValue = nullptr;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700282 result(http::status::not_modified);
Ed Tanous291d7092022-04-13 12:34:57 -0700283 }
284 }
285
286 void setExpectedHash(std::string_view hash)
287 {
288 expectedHash = hash;
289 }
290
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600291 bool openFile(const std::filesystem::path& path,
292 bmcweb::EncodingType enc = bmcweb::EncodingType::Raw)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700293 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700294 boost::beast::error_code ec;
Ed Tanous52e31622024-01-23 16:31:11 -0800295 response.body().open(path.c_str(), boost::beast::file_mode::read, ec);
296 response.body().encodingType = enc;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700297 if (ec)
298 {
Ed Tanous52e31622024-01-23 16:31:11 -0800299 BMCWEB_LOG_ERROR("Failed to open file {}", path.c_str());
Ed Tanous27b0cf92023-08-07 12:02:40 -0700300 return false;
301 }
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600302 return true;
303 }
304
305 bool openFd(int fd, bmcweb::EncodingType enc = bmcweb::EncodingType::Raw)
306 {
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600307 boost::beast::error_code ec;
Ed Tanous0242baf2024-05-16 19:52:47 -0700308 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
309 int retval = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
310 if (retval == -1)
311 {
312 BMCWEB_LOG_ERROR("Setting O_NONBLOCK failed");
313 }
Ed Tanous52e31622024-01-23 16:31:11 -0800314 response.body().encodingType = enc;
315 response.body().setFd(fd, ec);
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600316 if (ec)
317 {
318 BMCWEB_LOG_ERROR("Failed to set fd");
319 return false;
320 }
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600321 return true;
322 }
323
324 private:
Ed Tanous291d7092022-04-13 12:34:57 -0700325 std::optional<std::string> expectedHash;
Nan Zhou72374eb2022-01-27 17:06:51 -0800326 bool completed = false;
327 std::function<void(Response&)> completeRequestHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700328};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700329} // namespace crow