1f4c99e70SEd Tanous #pragma once
2f4c99e70SEd Tanous
3e796c262SNan Zhou #include "bmcweb_config.h"
4e796c262SNan Zhou
5e796c262SNan Zhou #include "app.hpp"
6e796c262SNan Zhou #include "async_resp.hpp"
7e796c262SNan Zhou #include "error_messages.hpp"
8e796c262SNan Zhou #include "http_request.hpp"
9e796c262SNan Zhou #include "http_response.hpp"
10e796c262SNan Zhou #include "logging.hpp"
11f4c99e70SEd Tanous #include "utils/query_param.hpp"
12f4c99e70SEd Tanous
13e796c262SNan Zhou #include <boost/beast/http/verb.hpp>
14e796c262SNan Zhou #include <boost/url/params_view.hpp>
15e796c262SNan Zhou #include <boost/url/url_view.hpp>
16e796c262SNan Zhou
17e796c262SNan Zhou #include <functional>
18e796c262SNan Zhou #include <memory>
19e796c262SNan Zhou #include <optional>
20e796c262SNan Zhou #include <string>
21e796c262SNan Zhou #include <string_view>
22e796c262SNan Zhou #include <type_traits>
23e796c262SNan Zhou #include <utility>
24e796c262SNan Zhou
25e796c262SNan Zhou // IWYU pragma: no_forward_declare crow::App
26f4c99e70SEd Tanous
273ccb3adbSEd Tanous #include "redfish_aggregator.hpp"
2805916cefSCarson Labrado
29f4c99e70SEd Tanous namespace redfish
30f4c99e70SEd Tanous {
afterIfMatchRequest(crow::App & app,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::shared_ptr<crow::Request> & req,const std::string & ifMatchHeader,const crow::Response & resIn)31102a4cdaSJonathan Doman inline void afterIfMatchRequest(
32102a4cdaSJonathan Doman crow::App& app, const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
33102a4cdaSJonathan Doman const std::shared_ptr<crow::Request>& req, const std::string& ifMatchHeader,
342d6cb56bSEd Tanous const crow::Response& resIn)
352d6cb56bSEd Tanous {
362d6cb56bSEd Tanous std::string computedEtag = resIn.computeEtag();
3762598e31SEd Tanous BMCWEB_LOG_DEBUG("User provided if-match etag {} computed etag {}",
3862598e31SEd Tanous ifMatchHeader, computedEtag);
392d6cb56bSEd Tanous if (computedEtag != ifMatchHeader)
402d6cb56bSEd Tanous {
412d6cb56bSEd Tanous messages::preconditionFailed(asyncResp->res);
422d6cb56bSEd Tanous return;
432d6cb56bSEd Tanous }
442d6cb56bSEd Tanous // Restart the request without if-match
45102a4cdaSJonathan Doman req->clearHeader(boost::beast::http::field::if_match);
4662598e31SEd Tanous BMCWEB_LOG_DEBUG("Restarting request");
472d6cb56bSEd Tanous app.handle(req, asyncResp);
482d6cb56bSEd Tanous }
492d6cb56bSEd Tanous
handleIfMatch(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)502d6cb56bSEd Tanous inline bool handleIfMatch(crow::App& app, const crow::Request& req,
512d6cb56bSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
522d6cb56bSEd Tanous {
532d6cb56bSEd Tanous if (req.session == nullptr)
542d6cb56bSEd Tanous {
552d6cb56bSEd Tanous // If the user isn't authenticated, don't even attempt to parse match
562d6cb56bSEd Tanous // parameters
572d6cb56bSEd Tanous return true;
582d6cb56bSEd Tanous }
592d6cb56bSEd Tanous
602d6cb56bSEd Tanous std::string ifMatch{
612d6cb56bSEd Tanous req.getHeaderValue(boost::beast::http::field::if_match)};
622d6cb56bSEd Tanous if (ifMatch.empty())
632d6cb56bSEd Tanous {
642d6cb56bSEd Tanous // No If-Match header. Nothing to do
652d6cb56bSEd Tanous return true;
662d6cb56bSEd Tanous }
67a1cbc192SHieu Huynh if (ifMatch == "*")
68a1cbc192SHieu Huynh {
69a1cbc192SHieu Huynh // Representing any resource
70a1cbc192SHieu Huynh // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match
71a1cbc192SHieu Huynh return true;
72a1cbc192SHieu Huynh }
731873a04fSMyung Bae if (req.method() != boost::beast::http::verb::patch &&
741873a04fSMyung Bae req.method() != boost::beast::http::verb::post &&
751873a04fSMyung Bae req.method() != boost::beast::http::verb::delete_)
762d6cb56bSEd Tanous {
772d6cb56bSEd Tanous messages::preconditionFailed(asyncResp->res);
782d6cb56bSEd Tanous return false;
792d6cb56bSEd Tanous }
802d6cb56bSEd Tanous boost::system::error_code ec;
812d6cb56bSEd Tanous
822d6cb56bSEd Tanous // Try to GET the same resource
83102a4cdaSJonathan Doman auto getReq = std::make_shared<crow::Request>(
84102a4cdaSJonathan Doman crow::Request::Body{boost::beast::http::verb::get,
85102a4cdaSJonathan Doman req.url().encoded_path(), 11},
86102a4cdaSJonathan Doman ec);
872d6cb56bSEd Tanous
882d6cb56bSEd Tanous if (ec)
892d6cb56bSEd Tanous {
902d6cb56bSEd Tanous messages::internalError(asyncResp->res);
912d6cb56bSEd Tanous return false;
922d6cb56bSEd Tanous }
932d6cb56bSEd Tanous
942d6cb56bSEd Tanous // New request has the same credentials as the old request
95102a4cdaSJonathan Doman getReq->session = req.session;
962d6cb56bSEd Tanous
972d6cb56bSEd Tanous // Construct a new response object to fill in, and check the hash of before
982d6cb56bSEd Tanous // we modify the Resource.
992d6cb56bSEd Tanous std::shared_ptr<bmcweb::AsyncResp> getReqAsyncResp =
1002d6cb56bSEd Tanous std::make_shared<bmcweb::AsyncResp>();
1012d6cb56bSEd Tanous
102102a4cdaSJonathan Doman // Ideally we would have a shared_ptr to the original Request which we could
103102a4cdaSJonathan Doman // modify to remove the If-Match and restart it. But instead we have to make
104102a4cdaSJonathan Doman // a full copy to restart it.
1052d6cb56bSEd Tanous getReqAsyncResp->res.setCompleteRequestHandler(std::bind_front(
106102a4cdaSJonathan Doman afterIfMatchRequest, std::ref(app), asyncResp,
107102a4cdaSJonathan Doman std::make_shared<crow::Request>(req), std::move(ifMatch)));
1082d6cb56bSEd Tanous
109102a4cdaSJonathan Doman app.handle(getReq, getReqAsyncResp);
1102d6cb56bSEd Tanous return false;
1112d6cb56bSEd Tanous }
112f4c99e70SEd Tanous
113a6b9125fSNan Zhou // Sets up the Redfish Route and delegates some of the query parameter
114a6b9125fSNan Zhou // processing. |queryCapabilities| stores which query parameters will be
115a6b9125fSNan Zhou // handled by redfish-core/lib codes, then default query parameter handler won't
116a6b9125fSNan Zhou // process these parameters.
setUpRedfishRouteWithDelegation(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,query_param::Query & delegated,const query_param::QueryCapabilities & queryCapabilities)117a6b9125fSNan Zhou [[nodiscard]] inline bool setUpRedfishRouteWithDelegation(
1183ba00073SCarson Labrado crow::App& app, const crow::Request& req,
1193ba00073SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
120a6b9125fSNan Zhou query_param::Query& delegated,
121a6b9125fSNan Zhou const query_param::QueryCapabilities& queryCapabilities)
122f4c99e70SEd Tanous {
12362598e31SEd Tanous BMCWEB_LOG_DEBUG("setup redfish route");
124142ec9aeSEd Tanous
125142ec9aeSEd Tanous // Section 7.4 of the redfish spec "Redfish Services shall process the
126142ec9aeSEd Tanous // [OData-Version header] in the following table as defined by the HTTP 1.1
127142ec9aeSEd Tanous // specification..."
128142ec9aeSEd Tanous // Required to pass redfish-protocol-validator REQ_HEADERS_ODATA_VERSION
129142ec9aeSEd Tanous std::string_view odataHeader = req.getHeaderValue("OData-Version");
130142ec9aeSEd Tanous if (!odataHeader.empty() && odataHeader != "4.0")
131142ec9aeSEd Tanous {
1323ba00073SCarson Labrado messages::preconditionFailed(asyncResp->res);
133142ec9aeSEd Tanous return false;
134142ec9aeSEd Tanous }
135142ec9aeSEd Tanous
1363ba00073SCarson Labrado asyncResp->res.addHeader("OData-Version", "4.0");
137c02a74f8SEd Tanous
138f4c99e70SEd Tanous std::optional<query_param::Query> queryOpt =
13939662a3bSEd Tanous query_param::parseParameters(req.url().params(), asyncResp->res);
140e01d0c36SEd Tanous if (!queryOpt)
141f4c99e70SEd Tanous {
142f4c99e70SEd Tanous return false;
143f4c99e70SEd Tanous }
144f4c99e70SEd Tanous
1452d6cb56bSEd Tanous if (!handleIfMatch(app, req, asyncResp))
1462d6cb56bSEd Tanous {
1472d6cb56bSEd Tanous return false;
1482d6cb56bSEd Tanous }
1492d6cb56bSEd Tanous
15005916cefSCarson Labrado bool needToCallHandlers = true;
15105916cefSCarson Labrado
15225b54dbaSEd Tanous if constexpr (BMCWEB_REDFISH_AGGREGATION)
15325b54dbaSEd Tanous {
154*bd79bce8SPatrick Williams needToCallHandlers =
155*bd79bce8SPatrick Williams RedfishAggregator::beginAggregation(req, asyncResp) ==
156*bd79bce8SPatrick Williams Result::LocalHandle;
15705916cefSCarson Labrado
15825b54dbaSEd Tanous // If the request should be forwarded to a satellite BMC then we don't
15925b54dbaSEd Tanous // want to write anything to the asyncResp since it will get overwritten
16025b54dbaSEd Tanous // later.
16125b54dbaSEd Tanous }
16205916cefSCarson Labrado
1637cf436c9SEd Tanous // If this isn't a get, no need to do anything with parameters
1647cf436c9SEd Tanous if (req.method() != boost::beast::http::verb::get)
1657cf436c9SEd Tanous {
16605916cefSCarson Labrado return needToCallHandlers;
1677cf436c9SEd Tanous }
1687cf436c9SEd Tanous
169a6b9125fSNan Zhou delegated = query_param::delegate(queryCapabilities, *queryOpt);
170f4c99e70SEd Tanous std::function<void(crow::Response&)> handler =
1713ba00073SCarson Labrado asyncResp->res.releaseCompleteRequestHandler();
172827c4902SNan Zhou
1733ba00073SCarson Labrado asyncResp->res.setCompleteRequestHandler(
17432cdb4a7SWilly Tu [&app, handler(std::move(handler)), query{std::move(*queryOpt)},
17532cdb4a7SWilly Tu delegated{delegated}](crow::Response& resIn) mutable {
17632cdb4a7SWilly Tu processAllParams(app, query, delegated, handler, resIn);
177f4c99e70SEd Tanous });
178827c4902SNan Zhou
17905916cefSCarson Labrado return needToCallHandlers;
180f4c99e70SEd Tanous }
181a6b9125fSNan Zhou
182a6b9125fSNan Zhou // Sets up the Redfish Route. All parameters are handled by the default handler.
1833ba00073SCarson Labrado [[nodiscard]] inline bool
setUpRedfishRoute(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1843ba00073SCarson Labrado setUpRedfishRoute(crow::App& app, const crow::Request& req,
1853ba00073SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
186a6b9125fSNan Zhou {
187a6b9125fSNan Zhou // This route |delegated| is never used
188a6b9125fSNan Zhou query_param::Query delegated;
1893ba00073SCarson Labrado return setUpRedfishRouteWithDelegation(app, req, asyncResp, delegated,
190a6b9125fSNan Zhou query_param::QueryCapabilities{});
191a6b9125fSNan Zhou }
192f4c99e70SEd Tanous } // namespace redfish
193