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 <new> 20e796c262SNan Zhou #include <optional> 21e796c262SNan Zhou #include <string> 22e796c262SNan Zhou #include <string_view> 23e796c262SNan Zhou #include <type_traits> 24e796c262SNan Zhou #include <utility> 25e796c262SNan Zhou 26e796c262SNan Zhou // IWYU pragma: no_forward_declare crow::App 27e796c262SNan Zhou // IWYU pragma: no_include <boost/url/impl/params_view.hpp> 28e796c262SNan Zhou // IWYU pragma: no_include <boost/url/impl/url_view.hpp> 29f4c99e70SEd Tanous 303ccb3adbSEd Tanous #include "redfish_aggregator.hpp" 3105916cefSCarson Labrado 32f4c99e70SEd Tanous namespace redfish 33f4c99e70SEd Tanous { 342d6cb56bSEd Tanous inline void 352d6cb56bSEd Tanous afterIfMatchRequest(crow::App& app, 362d6cb56bSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 372d6cb56bSEd Tanous crow::Request& req, const std::string& ifMatchHeader, 382d6cb56bSEd Tanous const crow::Response& resIn) 392d6cb56bSEd Tanous { 402d6cb56bSEd Tanous std::string computedEtag = resIn.computeEtag(); 4162598e31SEd Tanous BMCWEB_LOG_DEBUG("User provided if-match etag {} computed etag {}", 4262598e31SEd Tanous ifMatchHeader, computedEtag); 432d6cb56bSEd Tanous if (computedEtag != ifMatchHeader) 442d6cb56bSEd Tanous { 452d6cb56bSEd Tanous messages::preconditionFailed(asyncResp->res); 462d6cb56bSEd Tanous return; 472d6cb56bSEd Tanous } 482d6cb56bSEd Tanous // Restart the request without if-match 49*1873a04fSMyung Bae req.clearHeader(boost::beast::http::field::if_match); 5062598e31SEd Tanous BMCWEB_LOG_DEBUG("Restarting request"); 512d6cb56bSEd Tanous app.handle(req, asyncResp); 522d6cb56bSEd Tanous } 532d6cb56bSEd Tanous 542d6cb56bSEd Tanous inline bool handleIfMatch(crow::App& app, const crow::Request& req, 552d6cb56bSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 562d6cb56bSEd Tanous { 572d6cb56bSEd Tanous if (req.session == nullptr) 582d6cb56bSEd Tanous { 592d6cb56bSEd Tanous // If the user isn't authenticated, don't even attempt to parse match 602d6cb56bSEd Tanous // parameters 612d6cb56bSEd Tanous return true; 622d6cb56bSEd Tanous } 632d6cb56bSEd Tanous 642d6cb56bSEd Tanous std::string ifMatch{ 652d6cb56bSEd Tanous req.getHeaderValue(boost::beast::http::field::if_match)}; 662d6cb56bSEd Tanous if (ifMatch.empty()) 672d6cb56bSEd Tanous { 682d6cb56bSEd Tanous // No If-Match header. Nothing to do 692d6cb56bSEd Tanous return true; 702d6cb56bSEd Tanous } 71a1cbc192SHieu Huynh if (ifMatch == "*") 72a1cbc192SHieu Huynh { 73a1cbc192SHieu Huynh // Representing any resource 74a1cbc192SHieu Huynh // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Match 75a1cbc192SHieu Huynh return true; 76a1cbc192SHieu Huynh } 77*1873a04fSMyung Bae if (req.method() != boost::beast::http::verb::patch && 78*1873a04fSMyung Bae req.method() != boost::beast::http::verb::post && 79*1873a04fSMyung Bae req.method() != boost::beast::http::verb::delete_) 802d6cb56bSEd Tanous { 812d6cb56bSEd Tanous messages::preconditionFailed(asyncResp->res); 822d6cb56bSEd Tanous return false; 832d6cb56bSEd Tanous } 842d6cb56bSEd Tanous boost::system::error_code ec; 852d6cb56bSEd Tanous 862d6cb56bSEd Tanous // Try to GET the same resource 8739662a3bSEd Tanous crow::Request newReq( 8839662a3bSEd Tanous {boost::beast::http::verb::get, req.url().encoded_path(), 11}, ec); 892d6cb56bSEd Tanous 902d6cb56bSEd Tanous if (ec) 912d6cb56bSEd Tanous { 922d6cb56bSEd Tanous messages::internalError(asyncResp->res); 932d6cb56bSEd Tanous return false; 942d6cb56bSEd Tanous } 952d6cb56bSEd Tanous 962d6cb56bSEd Tanous // New request has the same credentials as the old request 972d6cb56bSEd Tanous newReq.session = req.session; 982d6cb56bSEd Tanous 992d6cb56bSEd Tanous // Construct a new response object to fill in, and check the hash of before 1002d6cb56bSEd Tanous // we modify the Resource. 1012d6cb56bSEd Tanous std::shared_ptr<bmcweb::AsyncResp> getReqAsyncResp = 1022d6cb56bSEd Tanous std::make_shared<bmcweb::AsyncResp>(); 1032d6cb56bSEd Tanous 1042d6cb56bSEd Tanous getReqAsyncResp->res.setCompleteRequestHandler(std::bind_front( 1052d6cb56bSEd Tanous afterIfMatchRequest, std::ref(app), asyncResp, req, ifMatch)); 1062d6cb56bSEd Tanous 1072d6cb56bSEd Tanous app.handle(newReq, getReqAsyncResp); 1082d6cb56bSEd Tanous return false; 1092d6cb56bSEd Tanous } 110f4c99e70SEd Tanous 111a6b9125fSNan Zhou // Sets up the Redfish Route and delegates some of the query parameter 112a6b9125fSNan Zhou // processing. |queryCapabilities| stores which query parameters will be 113a6b9125fSNan Zhou // handled by redfish-core/lib codes, then default query parameter handler won't 114a6b9125fSNan Zhou // process these parameters. 115a6b9125fSNan Zhou [[nodiscard]] inline bool setUpRedfishRouteWithDelegation( 1163ba00073SCarson Labrado crow::App& app, const crow::Request& req, 1173ba00073SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 118a6b9125fSNan Zhou query_param::Query& delegated, 119a6b9125fSNan Zhou const query_param::QueryCapabilities& queryCapabilities) 120f4c99e70SEd Tanous { 12162598e31SEd Tanous BMCWEB_LOG_DEBUG("setup redfish route"); 122142ec9aeSEd Tanous 123142ec9aeSEd Tanous // Section 7.4 of the redfish spec "Redfish Services shall process the 124142ec9aeSEd Tanous // [OData-Version header] in the following table as defined by the HTTP 1.1 125142ec9aeSEd Tanous // specification..." 126142ec9aeSEd Tanous // Required to pass redfish-protocol-validator REQ_HEADERS_ODATA_VERSION 127142ec9aeSEd Tanous std::string_view odataHeader = req.getHeaderValue("OData-Version"); 128142ec9aeSEd Tanous if (!odataHeader.empty() && odataHeader != "4.0") 129142ec9aeSEd Tanous { 1303ba00073SCarson Labrado messages::preconditionFailed(asyncResp->res); 131142ec9aeSEd Tanous return false; 132142ec9aeSEd Tanous } 133142ec9aeSEd Tanous 1343ba00073SCarson Labrado asyncResp->res.addHeader("OData-Version", "4.0"); 135c02a74f8SEd Tanous 136f4c99e70SEd Tanous std::optional<query_param::Query> queryOpt = 13739662a3bSEd Tanous query_param::parseParameters(req.url().params(), asyncResp->res); 138e01d0c36SEd Tanous if (!queryOpt) 139f4c99e70SEd Tanous { 140f4c99e70SEd Tanous return false; 141f4c99e70SEd Tanous } 142f4c99e70SEd Tanous 1432d6cb56bSEd Tanous if (!handleIfMatch(app, req, asyncResp)) 1442d6cb56bSEd Tanous { 1452d6cb56bSEd Tanous return false; 1462d6cb56bSEd Tanous } 1472d6cb56bSEd Tanous 14805916cefSCarson Labrado bool needToCallHandlers = true; 14905916cefSCarson Labrado 15005916cefSCarson Labrado #ifdef BMCWEB_ENABLE_REDFISH_AGGREGATION 15130806a2bSEd Tanous needToCallHandlers = RedfishAggregator::beginAggregation(req, asyncResp) == 15230806a2bSEd Tanous Result::LocalHandle; 15305916cefSCarson Labrado 15405916cefSCarson Labrado // If the request should be forwarded to a satellite BMC then we don't want 15505916cefSCarson Labrado // to write anything to the asyncResp since it will get overwritten later. 15605916cefSCarson Labrado #endif 15705916cefSCarson Labrado 1587cf436c9SEd Tanous // If this isn't a get, no need to do anything with parameters 1597cf436c9SEd Tanous if (req.method() != boost::beast::http::verb::get) 1607cf436c9SEd Tanous { 16105916cefSCarson Labrado return needToCallHandlers; 1627cf436c9SEd Tanous } 1637cf436c9SEd Tanous 164a6b9125fSNan Zhou delegated = query_param::delegate(queryCapabilities, *queryOpt); 165f4c99e70SEd Tanous std::function<void(crow::Response&)> handler = 1663ba00073SCarson Labrado asyncResp->res.releaseCompleteRequestHandler(); 167827c4902SNan Zhou 1683ba00073SCarson Labrado asyncResp->res.setCompleteRequestHandler( 16932cdb4a7SWilly Tu [&app, handler(std::move(handler)), query{std::move(*queryOpt)}, 17032cdb4a7SWilly Tu delegated{delegated}](crow::Response& resIn) mutable { 17132cdb4a7SWilly Tu processAllParams(app, query, delegated, handler, resIn); 172f4c99e70SEd Tanous }); 173827c4902SNan Zhou 17405916cefSCarson Labrado return needToCallHandlers; 175f4c99e70SEd Tanous } 176a6b9125fSNan Zhou 177a6b9125fSNan Zhou // Sets up the Redfish Route. All parameters are handled by the default handler. 1783ba00073SCarson Labrado [[nodiscard]] inline bool 1793ba00073SCarson Labrado setUpRedfishRoute(crow::App& app, const crow::Request& req, 1803ba00073SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 181a6b9125fSNan Zhou { 182a6b9125fSNan Zhou // This route |delegated| is never used 183a6b9125fSNan Zhou query_param::Query delegated; 1843ba00073SCarson Labrado return setUpRedfishRouteWithDelegation(app, req, asyncResp, delegated, 185a6b9125fSNan Zhou query_param::QueryCapabilities{}); 186a6b9125fSNan Zhou } 187f4c99e70SEd Tanous } // namespace redfish 188