blob: e6c9f72032823ee1a911a3c2365ea51180ee5820 [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>
Ed Tanousfaf100f2023-05-25 10:03:14 -070016#include <nlohmann/json.hpp>
Ed Tanous7045c8d2017-04-03 10:04:37 -070017
Ed Tanousd7857202025-01-28 15:32:26 -080018#include <cstddef>
19#include <cstdint>
20#include <filesystem>
21#include <functional>
Ed Tanous8a9a25c2021-05-11 14:50:58 -070022#include <optional>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050023#include <string>
Ed Tanous8a9a25c2021-05-11 14:50:58 -070024#include <string_view>
Abhilash Raju8e3f7032023-07-17 08:53:11 -050025#include <utility>
Ed Tanous0242baf2024-05-16 19:52:47 -070026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace crow
28{
Ed Tanouse0d918b2018-03-27 17:41:04 -070029
Ed Tanous52cc1122020-07-18 13:51:21 -070030template <typename Adaptor, typename Handler>
Ed Tanous7045c8d2017-04-03 10:04:37 -070031class Connection;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +010032
Ed Tanous27b0cf92023-08-07 12:02:40 -070033namespace http = boost::beast::http;
34
Myung Baed51c61b2024-09-13 10:35:34 -050035enum class OpenCode
36{
37 Success,
38 FileDoesNotExist,
39 InternalError,
40};
41
Ed Tanous1abe55e2018-09-05 08:30:59 -070042struct Response
43{
Ed Tanous52cc1122020-07-18 13:51:21 -070044 template <typename Adaptor, typename Handler>
Ed Tanous1abe55e2018-09-05 08:30:59 -070045 friend class crow::Connection;
Ed Tanous7045c8d2017-04-03 10:04:37 -070046
Ed Tanousb2896142024-01-31 15:25:47 -080047 http::response<bmcweb::HttpBody> response;
Ed Tanouse0d918b2018-03-27 17:41:04 -070048
Ed Tanous1abe55e2018-09-05 08:30:59 -070049 nlohmann::json jsonValue;
Ed Tanous27b0cf92023-08-07 12:02:40 -070050 using fields_type = http::header<false, http::fields>;
51 fields_type& fields()
52 {
Ed Tanous52e31622024-01-23 16:31:11 -080053 return response.base();
Ed Tanous27b0cf92023-08-07 12:02:40 -070054 }
55
56 const fields_type& fields() const
57 {
Ed Tanous52e31622024-01-23 16:31:11 -080058 return response.base();
Ed Tanous27b0cf92023-08-07 12:02:40 -070059 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070060
Ed Tanous26ccae32023-02-16 10:28:44 -080061 void addHeader(std::string_view key, std::string_view value)
Ed Tanous1abe55e2018-09-05 08:30:59 -070062 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070063 fields().insert(key, value);
Ed Tanous7045c8d2017-04-03 10:04:37 -070064 }
Ed Tanous1abe55e2018-09-05 08:30:59 -070065
Ed Tanous27b0cf92023-08-07 12:02:40 -070066 void addHeader(http::field key, std::string_view value)
Ed Tanous1abe55e2018-09-05 08:30:59 -070067 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070068 fields().insert(key, value);
Ed Tanous994fd862023-06-06 13:37:03 -070069 }
70
Ed Tanous27b0cf92023-08-07 12:02:40 -070071 void clearHeader(http::field key)
Ed Tanous994fd862023-06-06 13:37:03 -070072 {
Ed Tanous27b0cf92023-08-07 12:02:40 -070073 fields().erase(key);
Ed Tanous2cd4cc12018-07-25 10:51:19 -070074 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070075
Ed Tanous52e31622024-01-23 16:31:11 -080076 Response() = default;
Ed Tanous13548d82022-07-22 09:50:44 -070077 Response(Response&& res) noexcept :
Ed Tanous27b0cf92023-08-07 12:02:40 -070078 response(std::move(res.response)), jsonValue(std::move(res.jsonValue)),
79 completed(res.completed)
Ed Tanous13548d82022-07-22 09:50:44 -070080 {
Ed Tanous13548d82022-07-22 09:50:44 -070081 // See note in operator= move handler for why this is needed.
82 if (!res.completed)
83 {
84 completeRequestHandler = std::move(res.completeRequestHandler);
85 res.completeRequestHandler = nullptr;
86 }
Ed Tanous13548d82022-07-22 09:50:44 -070087 }
88
Ed Tanousecd6a3a2022-01-07 09:18:40 -080089 ~Response() = default;
90
91 Response(const Response&) = delete;
Ed Tanous1abe55e2018-09-05 08:30:59 -070092 Response& operator=(const Response& r) = delete;
93
94 Response& operator=(Response&& r) noexcept
95 {
Ed Tanous62598e32023-07-17 17:06:25 -070096 BMCWEB_LOG_DEBUG("Moving response containers; this: {}; other: {}",
97 logPtr(this), logPtr(&r));
Nan Zhou72374eb2022-01-27 17:06:51 -080098 if (this == &r)
99 {
100 return *this;
101 }
Ed Tanous27b0cf92023-08-07 12:02:40 -0700102 response = std::move(r.response);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 jsonValue = std::move(r.jsonValue);
Ed Tanous499b5b42024-04-06 08:39:18 -0700104 expectedHash = std::move(r.expectedHash);
Ed Tanous13548d82022-07-22 09:50:44 -0700105
106 // Only need to move completion handler if not already completed
107 // Note, there are cases where we might move out of a Response object
108 // while in a completion handler for that response object. This check
109 // is intended to prevent destructing the functor we are currently
110 // executing from in that case.
111 if (!r.completed)
112 {
113 completeRequestHandler = std::move(r.completeRequestHandler);
114 r.completeRequestHandler = nullptr;
115 }
116 else
117 {
118 completeRequestHandler = nullptr;
119 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700120 completed = r.completed;
121 return *this;
122 }
123
Nan Zhou3590bd12022-08-12 18:05:09 +0000124 void result(unsigned v)
125 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700126 fields().result(v);
Nan Zhou3590bd12022-08-12 18:05:09 +0000127 }
128
Ed Tanous27b0cf92023-08-07 12:02:40 -0700129 void result(http::status v)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700131 fields().result(v);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700132 }
133
Ed Tanous27b0cf92023-08-07 12:02:40 -0700134 void copyBody(const Response& res)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700135 {
Ed Tanous52e31622024-01-23 16:31:11 -0800136 response.body() = res.response.body();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700137 }
138
139 http::status result() const
140 {
141 return fields().result();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700142 }
143
Carson Labrado039a47e2022-04-05 16:03:20 +0000144 unsigned resultInt() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700145 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700146 return fields().result_int();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700147 }
148
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700149 std::string_view reason() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700150 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700151 return fields().reason();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700152 }
153
154 bool isCompleted() const noexcept
155 {
156 return completed;
157 }
158
Ed Tanous27b0cf92023-08-07 12:02:40 -0700159 const std::string* body()
Ed Tanous1abe55e2018-09-05 08:30:59 -0700160 {
Ed Tanous52e31622024-01-23 16:31:11 -0800161 return &response.body().str();
Ed Tanous1abe55e2018-09-05 08:30:59 -0700162 }
163
Carson Labrado46a81462022-04-27 21:11:37 +0000164 std::string_view getHeaderValue(std::string_view key) const
165 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700166 return fields()[key];
Carson Labrado46a81462022-04-27 21:11:37 +0000167 }
168
Ed Tanous499b5b42024-04-06 08:39:18 -0700169 std::string_view getHeaderValue(boost::beast::http::field key) const
170 {
171 return fields()[key];
172 }
173
Ed Tanous1abe55e2018-09-05 08:30:59 -0700174 void keepAlive(bool k)
175 {
Ed Tanous52e31622024-01-23 16:31:11 -0800176 response.keep_alive(k);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700177 }
178
Ed Tanousbb60f4d2022-06-27 10:39:09 -0700179 bool keepAlive() const
Ed Tanousceac6f72018-12-02 11:58:47 -0800180 {
Ed Tanous52e31622024-01-23 16:31:11 -0800181 return response.keep_alive();
Ed Tanous27b0cf92023-08-07 12:02:40 -0700182 }
183
Ed Tanous52e31622024-01-23 16:31:11 -0800184 std::optional<uint64_t> size()
185 {
186 return response.body().payloadSize();
187 }
188
189 void preparePayload()
Ed Tanous27b0cf92023-08-07 12:02:40 -0700190 {
191 // This code is a throw-free equivalent to
192 // beast::http::message::prepare_payload
Ed Tanous52e31622024-01-23 16:31:11 -0800193 std::optional<uint64_t> pSize = response.body().payloadSize();
Ed Tanous0242baf2024-05-16 19:52:47 -0700194
Ed Tanous27b0cf92023-08-07 12:02:40 -0700195 using http::status;
196 using http::status_class;
197 using http::to_status_class;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700198 bool is1XXReturn = to_status_class(result()) ==
199 status_class::informational;
Ed Tanous0242baf2024-05-16 19:52:47 -0700200 if (!pSize)
201 {
202 response.chunked(true);
203 return;
204 }
205 response.content_length(*pSize);
206
207 if (is1XXReturn || result() == status::no_content ||
208 result() == status::not_modified)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700209 {
210 BMCWEB_LOG_CRITICAL("{} Response content provided but code was "
211 "no-content or not_modified, which aren't "
212 "allowed to have a body",
213 logPtr(this));
Ed Tanous52e31622024-01-23 16:31:11 -0800214 response.content_length(0);
215 return;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700216 }
Ed Tanous23a21a12020-07-25 04:45:05 +0000217 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700218
219 void clear()
220 {
Ed Tanous62598e32023-07-17 17:06:25 -0700221 BMCWEB_LOG_DEBUG("{} Clearing response containers", logPtr(this));
Ed Tanous52e31622024-01-23 16:31:11 -0800222 response.clear();
Ed Tanous06fc9be2024-03-27 19:58:11 -0700223 response.body().clear();
Ed Tanous52e31622024-01-23 16:31:11 -0800224
Ed Tanousa6695a82023-05-16 11:39:18 -0700225 jsonValue = nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700226 completed = false;
Ed Tanous291d7092022-04-13 12:34:57 -0700227 expectedHash = std::nullopt;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228 }
229
Ed Tanous2d6cb562022-07-07 20:44:54 -0700230 std::string computeEtag() const
Ed Tanous1abe55e2018-09-05 08:30:59 -0700231 {
Ed Tanous89f18002022-03-24 18:38:24 -0700232 // Only set etag if this request succeeded
Ed Tanous27b0cf92023-08-07 12:02:40 -0700233 if (result() != http::status::ok)
Ed Tanous89f18002022-03-24 18:38:24 -0700234 {
Ed Tanous2d6cb562022-07-07 20:44:54 -0700235 return "";
236 }
237 // and the json response isn't empty
238 if (jsonValue.empty())
239 {
240 return "";
241 }
242 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
243 return "\"" + intToHexString(hashval, 8) + "\"";
244 }
245
Ed Tanous27b0cf92023-08-07 12:02:40 -0700246 void write(std::string&& bodyPart)
247 {
Ed Tanous52e31622024-01-23 16:31:11 -0800248 response.body().str() = std::move(bodyPart);
Ed Tanous27b0cf92023-08-07 12:02:40 -0700249 }
250
Ed Tanous2d6cb562022-07-07 20:44:54 -0700251 void end()
252 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700253 if (completed)
254 {
Ed Tanous62598e32023-07-17 17:06:25 -0700255 BMCWEB_LOG_ERROR("{} Response was ended twice", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700256 return;
257 }
258 completed = true;
Ed Tanous62598e32023-07-17 17:06:25 -0700259 BMCWEB_LOG_DEBUG("{} calling completion handler", logPtr(this));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700260 if (completeRequestHandler)
261 {
Ed Tanous62598e32023-07-17 17:06:25 -0700262 BMCWEB_LOG_DEBUG("{} completion handler was valid", logPtr(this));
Nan Zhou72374eb2022-01-27 17:06:51 -0800263 completeRequestHandler(*this);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700264 }
265 }
266
Nan Zhou72374eb2022-01-27 17:06:51 -0800267 void setCompleteRequestHandler(std::function<void(Response&)>&& handler)
John Edward Broadbent4147b8a2021-07-19 16:52:24 -0700268 {
Ed Tanous62598e32023-07-17 17:06:25 -0700269 BMCWEB_LOG_DEBUG("{} setting completion handler", logPtr(this));
Nan Zhou72374eb2022-01-27 17:06:51 -0800270 completeRequestHandler = std::move(handler);
Ed Tanous13548d82022-07-22 09:50:44 -0700271
272 // Now that we have a new completion handler attached, we're no longer
273 // complete
274 completed = false;
Nan Zhou72374eb2022-01-27 17:06:51 -0800275 }
276
277 std::function<void(Response&)> releaseCompleteRequestHandler()
278 {
Ed Tanous62598e32023-07-17 17:06:25 -0700279 BMCWEB_LOG_DEBUG("{} releasing completion handler{}", logPtr(this),
280 static_cast<bool>(completeRequestHandler));
Nan Zhou72374eb2022-01-27 17:06:51 -0800281 std::function<void(Response&)> ret = completeRequestHandler;
282 completeRequestHandler = nullptr;
Ed Tanous13548d82022-07-22 09:50:44 -0700283 completed = true;
Nan Zhou72374eb2022-01-27 17:06:51 -0800284 return ret;
285 }
286
Ed Tanous291d7092022-04-13 12:34:57 -0700287 void setHashAndHandleNotModified()
288 {
289 // Can only hash if we have content that's valid
Ed Tanous27b0cf92023-08-07 12:02:40 -0700290 if (jsonValue.empty() || result() != http::status::ok)
Ed Tanous291d7092022-04-13 12:34:57 -0700291 {
292 return;
293 }
294 size_t hashval = std::hash<nlohmann::json>{}(jsonValue);
295 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\"";
Ed Tanous27b0cf92023-08-07 12:02:40 -0700296 addHeader(http::field::etag, hexVal);
Ed Tanous291d7092022-04-13 12:34:57 -0700297 if (expectedHash && hexVal == *expectedHash)
298 {
Ed Tanousa6695a82023-05-16 11:39:18 -0700299 jsonValue = nullptr;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700300 result(http::status::not_modified);
Ed Tanous291d7092022-04-13 12:34:57 -0700301 }
302 }
303
304 void setExpectedHash(std::string_view hash)
305 {
306 expectedHash = hash;
307 }
308
Myung Baed51c61b2024-09-13 10:35:34 -0500309 OpenCode openFile(const std::filesystem::path& path,
310 bmcweb::EncodingType enc = bmcweb::EncodingType::Raw)
Ed Tanous27b0cf92023-08-07 12:02:40 -0700311 {
Ed Tanous27b0cf92023-08-07 12:02:40 -0700312 boost::beast::error_code ec;
Ed Tanous52e31622024-01-23 16:31:11 -0800313 response.body().open(path.c_str(), boost::beast::file_mode::read, ec);
314 response.body().encodingType = enc;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700315 if (ec)
316 {
Myung Baed51c61b2024-09-13 10:35:34 -0500317 BMCWEB_LOG_ERROR("Failed to open file {}, ec={}", path.c_str(),
318 ec.value());
319 if (ec.value() == boost::system::errc::no_such_file_or_directory)
320 {
321 return OpenCode::FileDoesNotExist;
322 }
323 return OpenCode::InternalError;
Ed Tanous27b0cf92023-08-07 12:02:40 -0700324 }
Myung Baed51c61b2024-09-13 10:35:34 -0500325 return OpenCode::Success;
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600326 }
327
328 bool openFd(int fd, bmcweb::EncodingType enc = bmcweb::EncodingType::Raw)
329 {
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600330 boost::beast::error_code ec;
Ed Tanous0242baf2024-05-16 19:52:47 -0700331 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
332 int retval = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
333 if (retval == -1)
334 {
335 BMCWEB_LOG_ERROR("Setting O_NONBLOCK failed");
336 }
Ed Tanous52e31622024-01-23 16:31:11 -0800337 response.body().encodingType = enc;
338 response.body().setFd(fd, ec);
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600339 if (ec)
340 {
341 BMCWEB_LOG_ERROR("Failed to set fd");
342 return false;
343 }
Abhilash Rajub5f288d2023-11-08 22:32:44 -0600344 return true;
345 }
346
347 private:
Ed Tanous291d7092022-04-13 12:34:57 -0700348 std::optional<std::string> expectedHash;
Nan Zhou72374eb2022-01-27 17:06:51 -0800349 bool completed = false;
350 std::function<void(Response&)> completeRequestHandler;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700351};
Ed Tanous1abe55e2018-09-05 08:30:59 -0700352} // namespace crow