blob: 2d280cb7871b25106fca092793495c93be97e27c [file] [log] [blame]
Ed Tanousf4c99e72021-10-04 17:02:43 -07001#pragma once
2
Nan Zhoue796c262022-08-02 19:56:29 +00003#include "bmcweb_config.h"
4
5#include "app.hpp"
6#include "async_resp.hpp"
7#include "error_messages.hpp"
8#include "http_request.hpp"
9#include "http_response.hpp"
10#include "logging.hpp"
Ed Tanousf4c99e72021-10-04 17:02:43 -070011#include "utils/query_param.hpp"
12
Nan Zhoue796c262022-08-02 19:56:29 +000013#include <boost/beast/http/verb.hpp>
14#include <boost/url/params_view.hpp>
15#include <boost/url/url_view.hpp>
16
17#include <functional>
18#include <memory>
Nan Zhoue796c262022-08-02 19:56:29 +000019#include <optional>
20#include <string>
21#include <string_view>
22#include <type_traits>
23#include <utility>
24
25// IWYU pragma: no_forward_declare crow::App
26// IWYU pragma: no_include <boost/url/impl/params_view.hpp>
27// IWYU pragma: no_include <boost/url/impl/url_view.hpp>
Ed Tanousf4c99e72021-10-04 17:02:43 -070028
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080029#include "redfish_aggregator.hpp"
Carson Labrado05916ce2022-08-01 19:38:18 +000030
Ed Tanousf4c99e72021-10-04 17:02:43 -070031namespace redfish
32{
Jonathan Doman102a4cd2024-04-15 16:56:23 -070033inline void afterIfMatchRequest(
34 crow::App& app, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
35 const std::shared_ptr<crow::Request>& req, const std::string& ifMatchHeader,
36 const crow::Response& resIn)
Ed Tanous2d6cb562022-07-07 20:44:54 -070037{
38 std::string computedEtag = resIn.computeEtag();
Ed Tanous62598e32023-07-17 17:06:25 -070039 BMCWEB_LOG_DEBUG("User provided if-match etag {} computed etag {}",
40 ifMatchHeader, computedEtag);
Ed Tanous2d6cb562022-07-07 20:44:54 -070041 if (computedEtag != ifMatchHeader)
42 {
43 messages::preconditionFailed(asyncResp->res);
44 return;
45 }
46 // Restart the request without if-match
Jonathan Doman102a4cd2024-04-15 16:56:23 -070047 req->clearHeader(boost::beast::http::field::if_match);
Ed Tanous62598e32023-07-17 17:06:25 -070048 BMCWEB_LOG_DEBUG("Restarting request");
Ed Tanous2d6cb562022-07-07 20:44:54 -070049 app.handle(req, asyncResp);
50}
51
52inline bool handleIfMatch(crow::App& app, const crow::Request& req,
53 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
54{
55 if (req.session == nullptr)
56 {
57 // If the user isn't authenticated, don't even attempt to parse match
58 // parameters
59 return true;
60 }
61
62 std::string ifMatch{
63 req.getHeaderValue(boost::beast::http::field::if_match)};
64 if (ifMatch.empty())
65 {
66 // No If-Match header. Nothing to do
67 return true;
68 }
Hieu Huynha1cbc192023-03-29 07:24:42 +000069 if (ifMatch == "*")
70 {
71 // Representing any resource
72 // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match
73 return true;
74 }
Myung Bae1873a042024-04-01 09:27:39 -050075 if (req.method() != boost::beast::http::verb::patch &&
76 req.method() != boost::beast::http::verb::post &&
77 req.method() != boost::beast::http::verb::delete_)
Ed Tanous2d6cb562022-07-07 20:44:54 -070078 {
79 messages::preconditionFailed(asyncResp->res);
80 return false;
81 }
82 boost::system::error_code ec;
83
84 // Try to GET the same resource
Jonathan Doman102a4cd2024-04-15 16:56:23 -070085 auto getReq = std::make_shared<crow::Request>(
86 crow::Request::Body{boost::beast::http::verb::get,
87 req.url().encoded_path(), 11},
88 ec);
Ed Tanous2d6cb562022-07-07 20:44:54 -070089
90 if (ec)
91 {
92 messages::internalError(asyncResp->res);
93 return false;
94 }
95
96 // New request has the same credentials as the old request
Jonathan Doman102a4cd2024-04-15 16:56:23 -070097 getReq->session = req.session;
Ed Tanous2d6cb562022-07-07 20:44:54 -070098
99 // Construct a new response object to fill in, and check the hash of before
100 // we modify the Resource.
101 std::shared_ptr<bmcweb::AsyncResp> getReqAsyncResp =
102 std::make_shared<bmcweb::AsyncResp>();
103
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700104 // Ideally we would have a shared_ptr to the original Request which we could
105 // modify to remove the If-Match and restart it. But instead we have to make
106 // a full copy to restart it.
Ed Tanous2d6cb562022-07-07 20:44:54 -0700107 getReqAsyncResp->res.setCompleteRequestHandler(std::bind_front(
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700108 afterIfMatchRequest, std::ref(app), asyncResp,
109 std::make_shared<crow::Request>(req), std::move(ifMatch)));
Ed Tanous2d6cb562022-07-07 20:44:54 -0700110
Jonathan Doman102a4cd2024-04-15 16:56:23 -0700111 app.handle(getReq, getReqAsyncResp);
Ed Tanous2d6cb562022-07-07 20:44:54 -0700112 return false;
113}
Ed Tanousf4c99e72021-10-04 17:02:43 -0700114
Nan Zhoua6b91252022-04-04 13:10:40 -0700115// Sets up the Redfish Route and delegates some of the query parameter
116// processing. |queryCapabilities| stores which query parameters will be
117// handled by redfish-core/lib codes, then default query parameter handler won't
118// process these parameters.
119[[nodiscard]] inline bool setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +0000120 crow::App& app, const crow::Request& req,
121 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Nan Zhoua6b91252022-04-04 13:10:40 -0700122 query_param::Query& delegated,
123 const query_param::QueryCapabilities& queryCapabilities)
Ed Tanousf4c99e72021-10-04 17:02:43 -0700124{
Ed Tanous62598e32023-07-17 17:06:25 -0700125 BMCWEB_LOG_DEBUG("setup redfish route");
Ed Tanous142ec9a2022-03-24 18:20:45 -0700126
127 // Section 7.4 of the redfish spec "Redfish Services shall process the
128 // [OData-Version header] in the following table as defined by the HTTP 1.1
129 // specification..."
130 // Required to pass redfish-protocol-validator REQ_HEADERS_ODATA_VERSION
131 std::string_view odataHeader = req.getHeaderValue("OData-Version");
132 if (!odataHeader.empty() && odataHeader != "4.0")
133 {
Carson Labrado3ba00072022-06-06 19:40:56 +0000134 messages::preconditionFailed(asyncResp->res);
Ed Tanous142ec9a2022-03-24 18:20:45 -0700135 return false;
136 }
137
Carson Labrado3ba00072022-06-06 19:40:56 +0000138 asyncResp->res.addHeader("OData-Version", "4.0");
Ed Tanousc02a74f2022-05-11 14:46:44 -0700139
Ed Tanousf4c99e72021-10-04 17:02:43 -0700140 std::optional<query_param::Query> queryOpt =
Ed Tanous39662a32023-02-06 15:09:46 -0800141 query_param::parseParameters(req.url().params(), asyncResp->res);
Ed Tanouse01d0c32023-06-30 13:21:32 -0700142 if (!queryOpt)
Ed Tanousf4c99e72021-10-04 17:02:43 -0700143 {
144 return false;
145 }
146
Ed Tanous2d6cb562022-07-07 20:44:54 -0700147 if (!handleIfMatch(app, req, asyncResp))
148 {
149 return false;
150 }
151
Carson Labrado05916ce2022-08-01 19:38:18 +0000152 bool needToCallHandlers = true;
153
Ed Tanous25b54db2024-04-17 15:40:31 -0700154 if constexpr (BMCWEB_REDFISH_AGGREGATION)
155 {
156 needToCallHandlers = RedfishAggregator::beginAggregation(
157 req, asyncResp) == Result::LocalHandle;
Carson Labrado05916ce2022-08-01 19:38:18 +0000158
Ed Tanous25b54db2024-04-17 15:40:31 -0700159 // If the request should be forwarded to a satellite BMC then we don't
160 // want to write anything to the asyncResp since it will get overwritten
161 // later.
162 }
Carson Labrado05916ce2022-08-01 19:38:18 +0000163
Ed Tanous7cf436c2022-03-22 23:53:51 -0700164 // If this isn't a get, no need to do anything with parameters
165 if (req.method() != boost::beast::http::verb::get)
166 {
Carson Labrado05916ce2022-08-01 19:38:18 +0000167 return needToCallHandlers;
Ed Tanous7cf436c2022-03-22 23:53:51 -0700168 }
169
Nan Zhoua6b91252022-04-04 13:10:40 -0700170 delegated = query_param::delegate(queryCapabilities, *queryOpt);
Ed Tanousf4c99e72021-10-04 17:02:43 -0700171 std::function<void(crow::Response&)> handler =
Carson Labrado3ba00072022-06-06 19:40:56 +0000172 asyncResp->res.releaseCompleteRequestHandler();
Nan Zhou827c4902022-08-03 04:57:55 +0000173
Carson Labrado3ba00072022-06-06 19:40:56 +0000174 asyncResp->res.setCompleteRequestHandler(
Willy Tu32cdb4a2023-05-23 10:58:39 -0700175 [&app, handler(std::move(handler)), query{std::move(*queryOpt)},
176 delegated{delegated}](crow::Response& resIn) mutable {
177 processAllParams(app, query, delegated, handler, resIn);
Ed Tanous002d39b2022-05-31 08:59:27 -0700178 });
Nan Zhou827c4902022-08-03 04:57:55 +0000179
Carson Labrado05916ce2022-08-01 19:38:18 +0000180 return needToCallHandlers;
Ed Tanousf4c99e72021-10-04 17:02:43 -0700181}
Nan Zhoua6b91252022-04-04 13:10:40 -0700182
183// Sets up the Redfish Route. All parameters are handled by the default handler.
Carson Labrado3ba00072022-06-06 19:40:56 +0000184[[nodiscard]] inline bool
185 setUpRedfishRoute(crow::App& app, const crow::Request& req,
186 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Nan Zhoua6b91252022-04-04 13:10:40 -0700187{
188 // This route |delegated| is never used
189 query_param::Query delegated;
Carson Labrado3ba00072022-06-06 19:40:56 +0000190 return setUpRedfishRouteWithDelegation(app, req, asyncResp, delegated,
Nan Zhoua6b91252022-04-04 13:10:40 -0700191 query_param::QueryCapabilities{});
192}
Ed Tanousf4c99e72021-10-04 17:02:43 -0700193} // namespace redfish