Handle redfish 404 with a good message

Not found 404 on redfish entries has always returned the generic, bmcweb
core driven 404, that didn't include a message.  This works most of the
time, because most things have BMCWEB_ROUTE entries, but there's a whole
class of routes that bmcweb doesn't implement, that should also return
404, with a resourceNotFound message.

This commit registers a generic /redfish/<path> route that forces things
to return 404.

One compromise made is that for a given route, if we don't know what
"type" the user was expecting back, there's no way to fill in the first
argument with the Redfish type, so this patchset opts for filling it in
with empty string "".  This is arguably incorrect, but still better than
what we had previously, so it seems like an acceptable compromise.

Tested:
curl -vvvv --insecure --user root:0penBmc https://192.168.7.2/redfish/v1/Systems
returns the SystemCollection resource as expected

curl -vvvv --insecure --user root:0penBmc https://192.168.7.2/redfish/v1/foo
Returns a ResourceNotFound message, as Redfish expects.

Signed-off-by: Ed Tanous <edtanous@google.com>
Change-Id: Ibef1ded004a950124104cf392b6bf9b679b3c85a
diff --git a/redfish-core/include/redfish.hpp b/redfish-core/include/redfish.hpp
index eae83e8..b7dd3b3 100644
--- a/redfish-core/include/redfish.hpp
+++ b/redfish-core/include/redfish.hpp
@@ -216,6 +216,7 @@
         requestRoutesTriggerCollection(app);
         requestRoutesTrigger(app);
 
+        // Note, this must be the last route registered
         requestRoutesRedfish(app);
     }
 };
diff --git a/redfish-core/lib/redfish_v1.hpp b/redfish-core/lib/redfish_v1.hpp
index 1b23d1c..cbd13b4 100644
--- a/redfish-core/lib/redfish_v1.hpp
+++ b/redfish-core/lib/redfish_v1.hpp
@@ -19,11 +19,37 @@
     asyncResp->res.jsonValue["v1"] = "/redfish/v1/";
 }
 
+inline void redfish404(App& app, const crow::Request& req,
+                       const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                       const std::string& path)
+{
+    asyncResp->res.addHeader(boost::beast::http::field::allow, "");
+
+    // If we fall to this route, we didn't have a more specific route, so return
+    // 404
+    if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
+    {
+        return;
+    }
+
+    BMCWEB_LOG_ERROR << "404 on path " << path;
+
+    boost::urls::string_value name = req.urlView.segments().back();
+    std::string_view nameStr(name.data(), name.size());
+    // Note, if we hit the wildcard route, we don't know the "type" the user was
+    // actually requesting, but giving them a return with an empty string is
+    // still better than nothing.
+    messages::resourceNotFound(asyncResp->res, "", nameStr);
+}
+
 inline void requestRoutesRedfish(App& app)
 {
     BMCWEB_ROUTE(app, "/redfish/")
         .methods(boost::beast::http::verb::get)(
             std::bind_front(redfishGet, std::ref(app)));
+
+    BMCWEB_ROUTE(app, "/redfish/<path>")
+    (std::bind_front(redfish404, std::ref(app)));
 }
 
 } // namespace redfish