ServiceRoot Support Link header

The Redfish standard in section 8.2 states:
A Link header containing rel=describedby shall be returned on GET and
HEAD requests for Redfish resources. If the referenced JSON Schema is a
versioned schema, it shall match the version contained in the value of
the @odata.type property returned in this resource.

This commit attempts to add this capability to ServiceRoot.  Future
similar patches will start adding this across the tree.

To do this, a few things happen.  First, this removes the implicit HEAD
handling in the router.  Because we now need explicit HEAD handling
per-route with specific headers, there's no good way to make this
generic.
Next, it rearranges the code such that handleServiceRootGet can first
call handleServiceRootHead, to avoid duplicating the addHeader call.

Tested: Redfish protocol validator passes the
RESP_HEADERS_REL_LINK_DESCRIBED_BY check for ServiceRoot.

Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: I92248089a3545432c14f551309ea62332e554647
diff --git a/redfish-core/lib/service_root.hpp b/redfish-core/lib/service_root.hpp
index 387b614..4afd650 100644
--- a/redfish-core/lib/service_root.hpp
+++ b/redfish-core/lib/service_root.hpp
@@ -30,7 +30,21 @@
 {
 
 inline void
-    handleServiceRootGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+    handleServiceRootHead(App& app, const crow::Request& req,
+                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+    {
+        return;
+    }
+
+    asyncResp->res.addHeader(
+        boost::beast::http::field::link,
+        "</redfish/v1/JsonSchemas/ServiceRoot/ServiceRoot.json>; rel=describedby");
+}
+
+inline void handleServiceRootGetImpl(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
 {
     std::string uuid = persistent_data::getConfig().systemUuid;
     asyncResp->res.jsonValue["@odata.type"] =
@@ -85,20 +99,24 @@
     protocolFeatures["DeepOperations"]["DeepPOST"] = false;
     protocolFeatures["DeepOperations"]["DeepPATCH"] = false;
 }
+inline void
+    handleServiceRootGet(App& app, const crow::Request& req,
+                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
+{
+    handleServiceRootHead(app, req, asyncResp);
+    handleServiceRootGetImpl(asyncResp);
+}
 
 inline void requestRoutesServiceRoot(App& app)
 {
     BMCWEB_ROUTE(app, "/redfish/v1/")
+        .privileges(redfish::privileges::headServiceRoot)
+        .methods(boost::beast::http::verb::head)(
+            std::bind_front(handleServiceRootHead, std::ref(app)));
+    BMCWEB_ROUTE(app, "/redfish/v1/")
         .privileges(redfish::privileges::getServiceRoot)
         .methods(boost::beast::http::verb::get)(
-            [&app](const crow::Request& req,
-                   const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
-        if (!redfish::setUpRedfishRoute(app, req, asyncResp))
-        {
-            return;
-        }
-        handleServiceRootGet(asyncResp);
-        });
+            std::bind_front(handleServiceRootGet, std::ref(app)));
 }
 
 } // namespace redfish