blob: 81c664a4f078bb4f7f2471547ce2df9a9908326d [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Ed Tanous7045c8d2017-04-03 10:04:37 -07003#pragma once
Ed Tanousb2896142024-01-31 15:25:47 -08004#include "http_body.hpp"
Ed Tanous04e438c2020-10-03 08:06:26 -07005#include "logging.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08006#include "utils/hex_utils.hpp"
Ed Tanous1abe55e2018-09-05 08:30:59 -07007
Ed Tanous0242baf2024-05-16 19:52:47 -07008#include <fcntl.h>
9
Ed Tanousd7857202025-01-28 15:32:26 -080010#include <boost/beast/core/error.hpp>
11#include <boost/beast/core/file_base.hpp>
12#include <boost/beast/http/field.hpp>
13#include <boost/beast/http/fields.hpp>
Ed Tanousd43cd0c2020-09-30 20:46:53 -070014#include <boost/beast/http/message.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080015#include <boost/beast/http/status.hpp>
Abiola Asojod23d6342025-06-18 20:15:24 +000016#include <boost/url/url_view.hpp>
Ed Tanousfaf100f2023-05-25 10:03:14 -070017#include <nlohmann/json.hpp>
Ed Tanous7045c8d2017-04-03 10:04:37 -070018
Ed Tanousd7857202025-01-28 15:32:26 -080019#include <cstddef>
20#include <cstdint>
21#include <filesystem>
22#include <functional>
Ed Tanous8a9a25c2021-05-11 14:50:58 -070023#include <optional>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050024#include <string>
Ed Tanous8a9a25c2021-05-11 14:50:58 -070025#include <string_view>
Abhilash Raju8e3f7032023-07-17 08:53:11 -050026#include <utility>
Ed Tanous0242baf2024-05-16 19:52:47 -070027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028namespace crow
29{
Ed Tanouse0d918b2018-03-27 17:41:04 -070030
Ed Tanous52cc1122020-07-18 13:51:21 -070031template <typename Adaptor, typename Handler>
Ed Tanous7045c8d2017-04-03 10:04:37 -070032class Connection;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010033
Ed Tanous27b0cf92023-08-07 12:02:40 -070034namespace http = boost::beast::http;
35
Myung Baed51c61b2024-09-13 10:35:34 -050036enum class OpenCode
37{
38 Success,
39 FileDoesNotExist,
40 InternalError,
41};
42
Ed Tanous1abe55e2018-09-05 08:30:59 -070043struct Response
44{
Ed Tanous52cc1122020-07-18 13:51:21 -070045 template <typename Adaptor, typename Handler>
Ed Tanous05c27352024-10-09 17:00:37 -070046 friend class Connection;
Ed Tanous7045c8d2017-04-03 10:04:37 -070047
Ed Tanousb2896142024-01-31 15:25:47 -080048 http::response<bmcweb::HttpBody> response;
Ed Tanouse0d918b2018-03-27 17:41:04 -070049
Ed Tanous1abe55e2018-09-05 08:30:59 -070050 nlohmann::json jsonValue;
Ed Tanous27b0cf92023-08-07 12:02:40 -070051 using fields_type = http::header<false, http::fields>;
52 fields_type& fields()
53 {
Ed Tanous52e31622024-01-23 16:31:11 -080054 return response.base();
Ed Tanous27b0cf92023-08-07 12:02:40 -070055 }
56
57 const fields_type& fields() const
58 {
Ed Tanous52e31622024-01-23 16:31:11 -080059 return response.base();
Ed Tanous27b0cf92023-08-07 12:02:40 -070060 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070061
Ed Tanous26ccae32023-02-16 10:28:44 -080062 void addHeader(std::string_view key, std::string_view value)
Ed Tanous1abe55e2018-09-05 08:30:59 -070063 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070064 fields().insert(key, value);
Ed Tanous7045c8d2017-04-03 10:04:37 -070065 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070066
Ed Tanous27b0cf92023-08-07 12:02:40 -070067 void addHeader(http::field key, std::string_view value)
Ed Tanous1abe55e2018-09-05 08:30:59 -070068 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070069 fields().insert(key, value);
Ed Tanous994fd862023-06-06 13:37:03 -070070 }
71
Ed Tanous27b0cf92023-08-07 12:02:40 -070072 void clearHeader(http::field key)
Ed Tanous994fd862023-06-06 13:37:03 -070073 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070074 fields().erase(key);
Ed Tanous2cd4cc12018-07-25 10:51:19 -070075 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070076
Ed Tanous52e31622024-01-23 16:31:11 -080077 Response() = default;
Ed Tanous13548d82022-07-22 09:50:44 -070078 Response(Response&& res) noexcept :
Ed Tanous27b0cf92023-08-07 12:02:40 -070079 response(std::move(res.response)), jsonValue(std::move(res.jsonValue)),
Corey Ethington08fad5d2025-07-31 12:14:27 -040080 requestExpectedEtag(std::move(res.requestExpectedEtag)),
81 currentOverrideEtag(std::move(res.currentOverrideEtag)),
82 completed(res.completed)
Ed Tanous13548d82022-07-22 09:50:44 -070083 {
Ed Tanous13548d82022-07-22 09:50:44 -070084 // See note in operator= move handler for why this is needed.
85 if (!res.completed)
86 {
87 completeRequestHandler = std::move(res.completeRequestHandler);
88 res.completeRequestHandler = nullptr;
89 }
Ed Tanous13548d82022-07-22 09:50:44 -070090 }
91
Ed Tanousecd6a3a2022-01-07 09:18:40 -080092 ~Response() = default;
93
94 Response(const Response&) = delete;
Ed Tanous1abe55e2018-09-05 08:30:59 -070095 Response& operator=(const Response& r) = delete;
96
97 Response& operator=(Response&& r) noexcept
98 {
Ed Tanous62598e32023-07-17 17:06:25 -070099 BMCWEB_LOG_DEBUG("Moving response containers; this: {}; other: {}",
100 logPtr(this), logPtr(&r));
Nan Zhou72374eb2022-01-27 17:06:51 -0800101 if (this == &r)
102 {
103 return *this;
104 }
Ed Tanous27b0cf92023-08-07 12:02:40 -0700105 response = std::move(r.response);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 jsonValue = std::move(r.jsonValue);
Corey Ethington08fad5d2025-07-31 12:14:27 -0400107 requestExpectedEtag = std::move(r.requestExpectedEtag);
108 currentOverrideEtag = std::move(r.currentOverrideEtag);
Ed Tanous13548d82022-07-22 09:50:44 -0700109
110 // Only need to move completion handler if not already completed
111 // Note, there are cases where we might move out of a Response object
112 // while in a completion handler for that response object. This check
113 // is intended to prevent destructing the functor we are currently
114 // executing from in that case.
115 if (!r.completed)
116 {
117 completeRequestHandler = std::move(r.completeRequestHandler);
118 r.completeRequestHandler = nullptr;
119 }
120 else
121 {
122 completeRequestHandler = nullptr;
123 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700124 completed = r.completed;
125 return *this;
126 }
127
Nan Zhou3590bd12022-08-12 18:05:09 +0000128 void result(unsigned v)
129 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700130 fields().result(v);
Nan Zhou3590bd12022-08-12 18:05:09 +0000131 }
132
Ed Tanous27b0cf92023-08-07 12:02:40 -0700133 void result(http::status v)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700135 fields().result(v);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700136 }
137
Ed Tanous27b0cf92023-08-07 12:02:40 -0700138 void copyBody(const Response& res)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 {
Ed Tanous52e31622024-01-23 16:31:11 -0800140 response.body() = res.response.body();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700141 }
142
143 http::status result() const
144 {
145 return fields().result();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700146 }
147
Carson Labrado039a47e2022-04-05 16:03:20 +0000148 unsigned resultInt() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700149 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700150 return fields().result_int();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700151 }
152
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700153 std::string_view reason() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700154 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700155 return fields().reason();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700156 }
157
158 bool isCompleted() const noexcept
159 {
160 return completed;
161 }
162
Ed Tanous27b0cf92023-08-07 12:02:40 -0700163 const std::string* body()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700164 {
Ed Tanous52e31622024-01-23 16:31:11 -0800165 return &response.body().str();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700166 }
167
Carson Labrado46a81462022-04-27 21:11:37 +0000168 std::string_view getHeaderValue(std::string_view key) const
169 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700170 return fields()[key];
Carson Labrado46a81462022-04-27 21:11:37 +0000171 }
172
Ed Tanous499b5b42024-04-06 08:39:18 -0700173 std::string_view getHeaderValue(boost::beast::http::field key) const
174 {
175 return fields()[key];
176 }
177
Ed Tanous1abe55e2018-09-05 08:30:59 -0700178 void keepAlive(bool k)
179 {
Ed Tanous52e31622024-01-23 16:31:11 -0800180 response.keep_alive(k);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700181 }
182
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700183 bool keepAlive() const
Ed Tanousceac6f72018-12-02 11:58:47 -0800184 {
Ed Tanous52e31622024-01-23 16:31:11 -0800185 return response.keep_alive();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700186 }
187
Ed Tanous52e31622024-01-23 16:31:11 -0800188 std::optional<uint64_t> size()
189 {
190 return response.body().payloadSize();
191 }
192
Abiola Asojod23d6342025-06-18 20:15:24 +0000193 void preparePayload(const boost::urls::url_view& urlView)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700194 {
Ed Tanous52e31622024-01-23 16:31:11 -0800195 std::optional<uint64_t> pSize = response.body().payloadSize();
Ed Tanous0242baf2024-05-16 19:52:47 -0700196
Ed Tanous27b0cf92023-08-07 12:02:40 -0700197 using http::status;
198 using http::status_class;
199 using http::to_status_class;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700200 bool is1XXReturn = to_status_class(result()) ==
201 status_class::informational;
Ed Tanous0242baf2024-05-16 19:52:47 -0700202 if (!pSize)
203 {
204 response.chunked(true);
205 return;
206 }
207 response.content_length(*pSize);
208
Abiola Asojo5070c7e2025-07-29 19:00:04 +0000209 if ((*pSize > 0) && (is1XXReturn || result() == status::no_content ||
210 result() == status::not_modified))
Ed Tanous27b0cf92023-08-07 12:02:40 -0700211 {
212 BMCWEB_LOG_CRITICAL("{} Response content provided but code was "
213 "no-content or not_modified, which aren't "
Abiola Asojod23d6342025-06-18 20:15:24 +0000214 "allowed to have a body for url : \"{}\"",
215 logPtr(this), urlView.path());
Ed Tanous52e31622024-01-23 16:31:11 -0800216 response.content_length(0);
217 return;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700218 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000219 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700220
221 void clear()
222 {
Ed Tanous62598e32023-07-17 17:06:25 -0700223 BMCWEB_LOG_DEBUG("{} Clearing response containers", logPtr(this));
Ed Tanous52e31622024-01-23 16:31:11 -0800224 response.clear();
Ed Tanous06fc9be2024-03-27 19:58:11 -0700225 response.body().clear();
Ed Tanous52e31622024-01-23 16:31:11 -0800226
Ed Tanousa6695a82023-05-16 11:39:18 -0700227 jsonValue = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228 completed = false;
Corey Ethington08fad5d2025-07-31 12:14:27 -0400229 requestExpectedEtag = std::nullopt;
230 currentOverrideEtag = std::nullopt;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700231 }
232
Corey Ethington08fad5d2025-07-31 12:14:27 -0400233 void setCurrentOverrideEtag(std::string_view newEtag)
234 {
235 if (currentOverrideEtag)
236 {
237 BMCWEB_LOG_WARNING(
238 "Response override etag was incorrectly set twice");
239 }
240 currentOverrideEtag = newEtag;
241 }
242
243 std::string getCurrentEtag() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700244 {
Ed Tanous89f18002022-03-24 18:38:24 -0700245 // Only set etag if this request succeeded
Ed Tanous27b0cf92023-08-07 12:02:40 -0700246 if (result() != http::status::ok)
Ed Tanous89f18002022-03-24 18:38:24 -0700247 {
Ed Tanous2d6cb562022-07-07 20:44:54 -0700248 return "";
249 }
250 // and the json response isn't empty
251 if (jsonValue.empty())
252 {
253 return "";
254 }
Corey Ethington08fad5d2025-07-31 12:14:27 -0400255
256 if (currentOverrideEtag)
257 {
258 return currentOverrideEtag.value();
259 }
260
Ed Tanous2d6cb562022-07-07 20:44:54 -0700261 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
262 return "\"" + intToHexString(hashval, 8) + "\"";
263 }
264
Ed Tanous27b0cf92023-08-07 12:02:40 -0700265 void write(std::string&& bodyPart)
266 {
Ed Tanous52e31622024-01-23 16:31:11 -0800267 response.body().str() = std::move(bodyPart);
Ed Tanous27b0cf92023-08-07 12:02:40 -0700268 }
269
Ed Tanous2d6cb562022-07-07 20:44:54 -0700270 void end()
271 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 if (completed)
273 {
Ed Tanous62598e32023-07-17 17:06:25 -0700274 BMCWEB_LOG_ERROR("{} Response was ended twice", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700275 return;
276 }
277 completed = true;
Ed Tanous62598e32023-07-17 17:06:25 -0700278 BMCWEB_LOG_DEBUG("{} calling completion handler", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279 if (completeRequestHandler)
280 {
Ed Tanous62598e32023-07-17 17:06:25 -0700281 BMCWEB_LOG_DEBUG("{} completion handler was valid", logPtr(this));
Nan Zhou72374eb2022-01-27 17:06:51 -0800282 completeRequestHandler(*this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700283 }
284 }
285
Nan Zhou72374eb2022-01-27 17:06:51 -0800286 void setCompleteRequestHandler(std::function<void(Response&)>&& handler)
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700287 {
Ed Tanous62598e32023-07-17 17:06:25 -0700288 BMCWEB_LOG_DEBUG("{} setting completion handler", logPtr(this));
Nan Zhou72374eb2022-01-27 17:06:51 -0800289 completeRequestHandler = std::move(handler);
Ed Tanous13548d82022-07-22 09:50:44 -0700290
291 // Now that we have a new completion handler attached, we're no longer
292 // complete
293 completed = false;
Nan Zhou72374eb2022-01-27 17:06:51 -0800294 }
295
296 std::function<void(Response&)> releaseCompleteRequestHandler()
297 {
Ed Tanous62598e32023-07-17 17:06:25 -0700298 BMCWEB_LOG_DEBUG("{} releasing completion handler{}", logPtr(this),
299 static_cast<bool>(completeRequestHandler));
Nan Zhou72374eb2022-01-27 17:06:51 -0800300 std::function<void(Response&)> ret = completeRequestHandler;
301 completeRequestHandler = nullptr;
Ed Tanous13548d82022-07-22 09:50:44 -0700302 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800303 return ret;
304 }
305
Corey Ethington08fad5d2025-07-31 12:14:27 -0400306 void setResponseEtagAndHandleNotModified()
Ed Tanous291d7092022-04-13 12:34:57 -0700307 {
308 // Can only hash if we have content that's valid
Ed Tanous27b0cf92023-08-07 12:02:40 -0700309 if (jsonValue.empty() || result() != http::status::ok)
Ed Tanous291d7092022-04-13 12:34:57 -0700310 {
311 return;
312 }
Corey Ethington08fad5d2025-07-31 12:14:27 -0400313 std::string hexVal = getCurrentEtag();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700314 addHeader(http::field::etag, hexVal);
Corey Ethington08fad5d2025-07-31 12:14:27 -0400315 if (requestExpectedEtag && hexVal == *requestExpectedEtag)
Ed Tanous291d7092022-04-13 12:34:57 -0700316 {
Ed Tanousa6695a82023-05-16 11:39:18 -0700317 jsonValue = nullptr;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700318 result(http::status::not_modified);
Ed Tanous291d7092022-04-13 12:34:57 -0700319 }
320 }
321
Corey Ethington08fad5d2025-07-31 12:14:27 -0400322 std::optional<std::string_view> getExpectedEtag() const
Ed Tanous291d7092022-04-13 12:34:57 -0700323 {
Corey Ethington08fad5d2025-07-31 12:14:27 -0400324 return requestExpectedEtag;
325 }
326
327 void setExpectedEtag(std::string_view etag)
328 {
329 if (requestExpectedEtag)
330 {
331 BMCWEB_LOG_WARNING(
332 "Request expected etag was incorrectly set twice");
333 }
334 requestExpectedEtag = etag;
Ed Tanous291d7092022-04-13 12:34:57 -0700335 }
336
Ed Tanousb2539062024-03-12 16:58:35 -0700337 OpenCode openFile(
338 const std::filesystem::path& path,
339 bmcweb::EncodingType enc = bmcweb::EncodingType::Raw,
340 bmcweb::CompressionType comp = bmcweb::CompressionType::Raw)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700341 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700342 boost::beast::error_code ec;
Ed Tanous52e31622024-01-23 16:31:11 -0800343 response.body().open(path.c_str(), boost::beast::file_mode::read, ec);
344 response.body().encodingType = enc;
Ed Tanousb2539062024-03-12 16:58:35 -0700345 response.body().compressionType = comp;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700346 if (ec)
347 {
Myung Baed51c61b2024-09-13 10:35:34 -0500348 BMCWEB_LOG_ERROR("Failed to open file {}, ec={}", path.c_str(),
349 ec.value());
350 if (ec.value() == boost::system::errc::no_such_file_or_directory)
351 {
352 return OpenCode::FileDoesNotExist;
353 }
354 return OpenCode::InternalError;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700355 }
Myung Baed51c61b2024-09-13 10:35:34 -0500356 return OpenCode::Success;
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600357 }
358
359 bool openFd(int fd, bmcweb::EncodingType enc = bmcweb::EncodingType::Raw)
360 {
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600361 boost::beast::error_code ec;
Ed Tanous0242baf2024-05-16 19:52:47 -0700362 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
363 int retval = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
364 if (retval == -1)
365 {
366 BMCWEB_LOG_ERROR("Setting O_NONBLOCK failed");
367 }
Ed Tanous52e31622024-01-23 16:31:11 -0800368 response.body().encodingType = enc;
369 response.body().setFd(fd, ec);
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600370 if (ec)
371 {
372 BMCWEB_LOG_ERROR("Failed to set fd");
373 return false;
374 }
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600375 return true;
376 }
377
378 private:
Corey Ethington08fad5d2025-07-31 12:14:27 -0400379 std::optional<std::string> requestExpectedEtag;
380 std::optional<std::string> currentOverrideEtag;
Nan Zhou72374eb2022-01-27 17:06:51 -0800381 bool completed = false;
382 std::function<void(Response&)> completeRequestHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700383};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700384} // namespace crow