blob: c5d537b2d5aed3384ff3b5d8dba57a824088e9e6 [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>
19#include <new>
20#include <optional>
21#include <string>
22#include <string_view>
23#include <type_traits>
24#include <utility>
25
26// IWYU pragma: no_forward_declare crow::App
27// IWYU pragma: no_include <boost/url/impl/params_view.hpp>
28// IWYU pragma: no_include <boost/url/impl/url_view.hpp>
Ed Tanousf4c99e72021-10-04 17:02:43 -070029
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080030#include "redfish_aggregator.hpp"
Carson Labrado05916ce2022-08-01 19:38:18 +000031
Ed Tanousf4c99e72021-10-04 17:02:43 -070032namespace redfish
33{
Ed Tanous2d6cb562022-07-07 20:44:54 -070034inline void
35 afterIfMatchRequest(crow::App& app,
36 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
37 crow::Request& req, const std::string& ifMatchHeader,
38 const crow::Response& resIn)
39{
40 std::string computedEtag = resIn.computeEtag();
41 BMCWEB_LOG_DEBUG << "User provided if-match etag " << ifMatchHeader
42 << " computed etag " << computedEtag;
43 if (computedEtag != ifMatchHeader)
44 {
45 messages::preconditionFailed(asyncResp->res);
46 return;
47 }
48 // Restart the request without if-match
49 req.req.erase(boost::beast::http::field::if_match);
50 BMCWEB_LOG_DEBUG << "Restarting request";
51 app.handle(req, asyncResp);
52}
53
54inline bool handleIfMatch(crow::App& app, const crow::Request& req,
55 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
56{
57 if (req.session == nullptr)
58 {
59 // If the user isn't authenticated, don't even attempt to parse match
60 // parameters
61 return true;
62 }
63
64 std::string ifMatch{
65 req.getHeaderValue(boost::beast::http::field::if_match)};
66 if (ifMatch.empty())
67 {
68 // No If-Match header. Nothing to do
69 return true;
70 }
71 if (req.req.method() != boost::beast::http::verb::patch &&
72 req.req.method() != boost::beast::http::verb::post &&
73 req.req.method() != boost::beast::http::verb::delete_)
74 {
75 messages::preconditionFailed(asyncResp->res);
76 return false;
77 }
78 boost::system::error_code ec;
79
80 // Try to GET the same resource
Ed Tanous39662a32023-02-06 15:09:46 -080081 crow::Request newReq(
82 {boost::beast::http::verb::get, req.url().encoded_path(), 11}, ec);
Ed Tanous2d6cb562022-07-07 20:44:54 -070083
84 if (ec)
85 {
86 messages::internalError(asyncResp->res);
87 return false;
88 }
89
90 // New request has the same credentials as the old request
91 newReq.session = req.session;
92
93 // Construct a new response object to fill in, and check the hash of before
94 // we modify the Resource.
95 std::shared_ptr<bmcweb::AsyncResp> getReqAsyncResp =
96 std::make_shared<bmcweb::AsyncResp>();
97
98 getReqAsyncResp->res.setCompleteRequestHandler(std::bind_front(
99 afterIfMatchRequest, std::ref(app), asyncResp, req, ifMatch));
100
101 app.handle(newReq, getReqAsyncResp);
102 return false;
103}
Ed Tanousf4c99e72021-10-04 17:02:43 -0700104
Nan Zhoua6b91252022-04-04 13:10:40 -0700105// Sets up the Redfish Route and delegates some of the query parameter
106// processing. |queryCapabilities| stores which query parameters will be
107// handled by redfish-core/lib codes, then default query parameter handler won't
108// process these parameters.
109[[nodiscard]] inline bool setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +0000110 crow::App& app, const crow::Request& req,
111 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Nan Zhoua6b91252022-04-04 13:10:40 -0700112 query_param::Query& delegated,
113 const query_param::QueryCapabilities& queryCapabilities)
Ed Tanousf4c99e72021-10-04 17:02:43 -0700114{
Ed Tanous142ec9a2022-03-24 18:20:45 -0700115 BMCWEB_LOG_DEBUG << "setup redfish route";
116
117 // Section 7.4 of the redfish spec "Redfish Services shall process the
118 // [OData-Version header] in the following table as defined by the HTTP 1.1
119 // specification..."
120 // Required to pass redfish-protocol-validator REQ_HEADERS_ODATA_VERSION
121 std::string_view odataHeader = req.getHeaderValue("OData-Version");
122 if (!odataHeader.empty() && odataHeader != "4.0")
123 {
Carson Labrado3ba00072022-06-06 19:40:56 +0000124 messages::preconditionFailed(asyncResp->res);
Ed Tanous142ec9a2022-03-24 18:20:45 -0700125 return false;
126 }
127
Carson Labrado3ba00072022-06-06 19:40:56 +0000128 asyncResp->res.addHeader("OData-Version", "4.0");
Ed Tanousc02a74f2022-05-11 14:46:44 -0700129
Ed Tanousf4c99e72021-10-04 17:02:43 -0700130 std::optional<query_param::Query> queryOpt =
Ed Tanous39662a32023-02-06 15:09:46 -0800131 query_param::parseParameters(req.url().params(), asyncResp->res);
Ed Tanousf4c99e72021-10-04 17:02:43 -0700132 if (queryOpt == std::nullopt)
133 {
134 return false;
135 }
136
Ed Tanous2d6cb562022-07-07 20:44:54 -0700137 if (!handleIfMatch(app, req, asyncResp))
138 {
139 return false;
140 }
141
Carson Labrado05916ce2022-08-01 19:38:18 +0000142 bool needToCallHandlers = true;
143
144#ifdef BMCWEB_ENABLE_REDFISH_AGGREGATION
145 needToCallHandlers = RedfishAggregator::getInstance().beginAggregation(
146 req, asyncResp) == Result::LocalHandle;
147
148 // If the request should be forwarded to a satellite BMC then we don't want
149 // to write anything to the asyncResp since it will get overwritten later.
150#endif
151
Ed Tanous7cf436c2022-03-22 23:53:51 -0700152 // If this isn't a get, no need to do anything with parameters
153 if (req.method() != boost::beast::http::verb::get)
154 {
Carson Labrado05916ce2022-08-01 19:38:18 +0000155 return needToCallHandlers;
Ed Tanous7cf436c2022-03-22 23:53:51 -0700156 }
157
Nan Zhoua6b91252022-04-04 13:10:40 -0700158 delegated = query_param::delegate(queryCapabilities, *queryOpt);
Ed Tanousf4c99e72021-10-04 17:02:43 -0700159 std::function<void(crow::Response&)> handler =
Carson Labrado3ba00072022-06-06 19:40:56 +0000160 asyncResp->res.releaseCompleteRequestHandler();
Nan Zhou827c4902022-08-03 04:57:55 +0000161
Carson Labrado3ba00072022-06-06 19:40:56 +0000162 asyncResp->res.setCompleteRequestHandler(
Ed Tanousf4c99e72021-10-04 17:02:43 -0700163 [&app, handler(std::move(handler)),
Nan Zhou827c4902022-08-03 04:57:55 +0000164 query{std::move(*queryOpt)}](crow::Response& resIn) mutable {
Ed Tanous8a592812022-06-04 09:06:59 -0700165 processAllParams(app, query, handler, resIn);
Ed Tanous002d39b2022-05-31 08:59:27 -0700166 });
Nan Zhou827c4902022-08-03 04:57:55 +0000167
Carson Labrado05916ce2022-08-01 19:38:18 +0000168 return needToCallHandlers;
Ed Tanousf4c99e72021-10-04 17:02:43 -0700169}
Nan Zhoua6b91252022-04-04 13:10:40 -0700170
171// Sets up the Redfish Route. All parameters are handled by the default handler.
Carson Labrado3ba00072022-06-06 19:40:56 +0000172[[nodiscard]] inline bool
173 setUpRedfishRoute(crow::App& app, const crow::Request& req,
174 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Nan Zhoua6b91252022-04-04 13:10:40 -0700175{
176 // This route |delegated| is never used
177 query_param::Query delegated;
Carson Labrado3ba00072022-06-06 19:40:56 +0000178 return setUpRedfishRouteWithDelegation(app, req, asyncResp, delegated,
Nan Zhoua6b91252022-04-04 13:10:40 -0700179 query_param::QueryCapabilities{});
180}
Ed Tanousf4c99e72021-10-04 17:02:43 -0700181} // namespace redfish