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 3005916cefSCarson Labrado #include <redfish_aggregator.hpp> 3105916cefSCarson Labrado 32f4c99e70SEd Tanous namespace redfish 33f4c99e70SEd Tanous { 34*2d6cb56bSEd Tanous inline void 35*2d6cb56bSEd Tanous afterIfMatchRequest(crow::App& app, 36*2d6cb56bSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 37*2d6cb56bSEd Tanous crow::Request& req, const std::string& ifMatchHeader, 38*2d6cb56bSEd Tanous const crow::Response& resIn) 39*2d6cb56bSEd Tanous { 40*2d6cb56bSEd Tanous std::string computedEtag = resIn.computeEtag(); 41*2d6cb56bSEd Tanous BMCWEB_LOG_DEBUG << "User provided if-match etag " << ifMatchHeader 42*2d6cb56bSEd Tanous << " computed etag " << computedEtag; 43*2d6cb56bSEd Tanous if (computedEtag != ifMatchHeader) 44*2d6cb56bSEd Tanous { 45*2d6cb56bSEd Tanous messages::preconditionFailed(asyncResp->res); 46*2d6cb56bSEd Tanous return; 47*2d6cb56bSEd Tanous } 48*2d6cb56bSEd Tanous // Restart the request without if-match 49*2d6cb56bSEd Tanous req.req.erase(boost::beast::http::field::if_match); 50*2d6cb56bSEd Tanous BMCWEB_LOG_DEBUG << "Restarting request"; 51*2d6cb56bSEd Tanous app.handle(req, asyncResp); 52*2d6cb56bSEd Tanous } 53*2d6cb56bSEd Tanous 54*2d6cb56bSEd Tanous inline bool handleIfMatch(crow::App& app, const crow::Request& req, 55*2d6cb56bSEd Tanous const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 56*2d6cb56bSEd Tanous { 57*2d6cb56bSEd Tanous if (req.session == nullptr) 58*2d6cb56bSEd Tanous { 59*2d6cb56bSEd Tanous // If the user isn't authenticated, don't even attempt to parse match 60*2d6cb56bSEd Tanous // parameters 61*2d6cb56bSEd Tanous return true; 62*2d6cb56bSEd Tanous } 63*2d6cb56bSEd Tanous 64*2d6cb56bSEd Tanous std::string ifMatch{ 65*2d6cb56bSEd Tanous req.getHeaderValue(boost::beast::http::field::if_match)}; 66*2d6cb56bSEd Tanous if (ifMatch.empty()) 67*2d6cb56bSEd Tanous { 68*2d6cb56bSEd Tanous // No If-Match header. Nothing to do 69*2d6cb56bSEd Tanous return true; 70*2d6cb56bSEd Tanous } 71*2d6cb56bSEd Tanous if (req.req.method() != boost::beast::http::verb::patch && 72*2d6cb56bSEd Tanous req.req.method() != boost::beast::http::verb::post && 73*2d6cb56bSEd Tanous req.req.method() != boost::beast::http::verb::delete_) 74*2d6cb56bSEd Tanous { 75*2d6cb56bSEd Tanous messages::preconditionFailed(asyncResp->res); 76*2d6cb56bSEd Tanous return false; 77*2d6cb56bSEd Tanous } 78*2d6cb56bSEd Tanous boost::system::error_code ec; 79*2d6cb56bSEd Tanous 80*2d6cb56bSEd Tanous // Try to GET the same resource 81*2d6cb56bSEd Tanous crow::Request newReq({boost::beast::http::verb::get, req.url, 11}, ec); 82*2d6cb56bSEd Tanous 83*2d6cb56bSEd Tanous if (ec) 84*2d6cb56bSEd Tanous { 85*2d6cb56bSEd Tanous messages::internalError(asyncResp->res); 86*2d6cb56bSEd Tanous return false; 87*2d6cb56bSEd Tanous } 88*2d6cb56bSEd Tanous 89*2d6cb56bSEd Tanous // New request has the same credentials as the old request 90*2d6cb56bSEd Tanous newReq.session = req.session; 91*2d6cb56bSEd Tanous 92*2d6cb56bSEd Tanous // Construct a new response object to fill in, and check the hash of before 93*2d6cb56bSEd Tanous // we modify the Resource. 94*2d6cb56bSEd Tanous std::shared_ptr<bmcweb::AsyncResp> getReqAsyncResp = 95*2d6cb56bSEd Tanous std::make_shared<bmcweb::AsyncResp>(); 96*2d6cb56bSEd Tanous 97*2d6cb56bSEd Tanous getReqAsyncResp->res.setCompleteRequestHandler(std::bind_front( 98*2d6cb56bSEd Tanous afterIfMatchRequest, std::ref(app), asyncResp, req, ifMatch)); 99*2d6cb56bSEd Tanous 100*2d6cb56bSEd Tanous app.handle(newReq, getReqAsyncResp); 101*2d6cb56bSEd Tanous return false; 102*2d6cb56bSEd Tanous } 103f4c99e70SEd Tanous 104a6b9125fSNan Zhou // Sets up the Redfish Route and delegates some of the query parameter 105a6b9125fSNan Zhou // processing. |queryCapabilities| stores which query parameters will be 106a6b9125fSNan Zhou // handled by redfish-core/lib codes, then default query parameter handler won't 107a6b9125fSNan Zhou // process these parameters. 108a6b9125fSNan Zhou [[nodiscard]] inline bool setUpRedfishRouteWithDelegation( 1093ba00073SCarson Labrado crow::App& app, const crow::Request& req, 1103ba00073SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 111a6b9125fSNan Zhou query_param::Query& delegated, 112a6b9125fSNan Zhou const query_param::QueryCapabilities& queryCapabilities) 113f4c99e70SEd Tanous { 114142ec9aeSEd Tanous BMCWEB_LOG_DEBUG << "setup redfish route"; 115142ec9aeSEd Tanous 116142ec9aeSEd Tanous // Section 7.4 of the redfish spec "Redfish Services shall process the 117142ec9aeSEd Tanous // [OData-Version header] in the following table as defined by the HTTP 1.1 118142ec9aeSEd Tanous // specification..." 119142ec9aeSEd Tanous // Required to pass redfish-protocol-validator REQ_HEADERS_ODATA_VERSION 120142ec9aeSEd Tanous std::string_view odataHeader = req.getHeaderValue("OData-Version"); 121142ec9aeSEd Tanous if (!odataHeader.empty() && odataHeader != "4.0") 122142ec9aeSEd Tanous { 1233ba00073SCarson Labrado messages::preconditionFailed(asyncResp->res); 124142ec9aeSEd Tanous return false; 125142ec9aeSEd Tanous } 126142ec9aeSEd Tanous 1273ba00073SCarson Labrado asyncResp->res.addHeader("OData-Version", "4.0"); 128c02a74f8SEd Tanous 129f4c99e70SEd Tanous std::optional<query_param::Query> queryOpt = 1303ba00073SCarson Labrado query_param::parseParameters(req.urlView.params(), asyncResp->res); 131f4c99e70SEd Tanous if (queryOpt == std::nullopt) 132f4c99e70SEd Tanous { 133f4c99e70SEd Tanous return false; 134f4c99e70SEd Tanous } 135f4c99e70SEd Tanous 136*2d6cb56bSEd Tanous if (!handleIfMatch(app, req, asyncResp)) 137*2d6cb56bSEd Tanous { 138*2d6cb56bSEd Tanous return false; 139*2d6cb56bSEd Tanous } 140*2d6cb56bSEd Tanous 14105916cefSCarson Labrado bool needToCallHandlers = true; 14205916cefSCarson Labrado 14305916cefSCarson Labrado #ifdef BMCWEB_ENABLE_REDFISH_AGGREGATION 14405916cefSCarson Labrado needToCallHandlers = RedfishAggregator::getInstance().beginAggregation( 14505916cefSCarson Labrado req, asyncResp) == Result::LocalHandle; 14605916cefSCarson Labrado 14705916cefSCarson Labrado // If the request should be forwarded to a satellite BMC then we don't want 14805916cefSCarson Labrado // to write anything to the asyncResp since it will get overwritten later. 14905916cefSCarson Labrado #endif 15005916cefSCarson Labrado 1517cf436c9SEd Tanous // If this isn't a get, no need to do anything with parameters 1527cf436c9SEd Tanous if (req.method() != boost::beast::http::verb::get) 1537cf436c9SEd Tanous { 15405916cefSCarson Labrado return needToCallHandlers; 1557cf436c9SEd Tanous } 1567cf436c9SEd Tanous 157a6b9125fSNan Zhou delegated = query_param::delegate(queryCapabilities, *queryOpt); 158f4c99e70SEd Tanous std::function<void(crow::Response&)> handler = 1593ba00073SCarson Labrado asyncResp->res.releaseCompleteRequestHandler(); 160827c4902SNan Zhou 1613ba00073SCarson Labrado asyncResp->res.setCompleteRequestHandler( 162f4c99e70SEd Tanous [&app, handler(std::move(handler)), 163827c4902SNan Zhou query{std::move(*queryOpt)}](crow::Response& resIn) mutable { 1648a592810SEd Tanous processAllParams(app, query, handler, resIn); 165f4c99e70SEd Tanous }); 166827c4902SNan Zhou 16705916cefSCarson Labrado return needToCallHandlers; 168f4c99e70SEd Tanous } 169a6b9125fSNan Zhou 170a6b9125fSNan Zhou // Sets up the Redfish Route. All parameters are handled by the default handler. 1713ba00073SCarson Labrado [[nodiscard]] inline bool 1723ba00073SCarson Labrado setUpRedfishRoute(crow::App& app, const crow::Request& req, 1733ba00073SCarson Labrado const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 174a6b9125fSNan Zhou { 175a6b9125fSNan Zhou // This route |delegated| is never used 176a6b9125fSNan Zhou query_param::Query delegated; 1773ba00073SCarson Labrado return setUpRedfishRouteWithDelegation(app, req, asyncResp, delegated, 178a6b9125fSNan Zhou query_param::QueryCapabilities{}); 179a6b9125fSNan Zhou } 180f4c99e70SEd Tanous } // namespace redfish 181