| #include "smart_router.h" | |
| #include <cstddef> | |
| #include <iostream> | |
| #include <memory> | |
| #include <string> | |
| #include <string_view> | |
| #include <vector> | |
| #include "absl/container/flat_hash_map.h" | |
| #include "absl/container/flat_hash_set.h" | |
| #include "absl/strings/str_split.h" | |
| #include "absl/synchronization/mutex.h" | |
| #include "http_request.hpp" | |
| #include "async_resp.hpp" | |
| #include "tlbmc/hal/shared_mem/server_interface.h" | |
| #include "tlbmc/hal/shared_mem/static_server_impl.h" | |
| #include "tlbmc/redfish/verb.h" | |
| #include "app_interface.h" | |
| #include "routing.hpp" | |
| // DO NOT MODIFY THIS FILE IN GERRIT. THIS FILE SHOULD ONLY BE MODIFIED IN G3 | |
| namespace crow { | |
| using ::milotic_tlbmc::StaticSharedMemoryServer; | |
| namespace internal { | |
| void SimpleTrie::AddSubtree(std::string_view subtree) { | |
| std::vector<std::string_view> parts = | |
| absl::StrSplit(subtree, '/', absl::SkipEmpty()); | |
| Node* node = &root_; | |
| for (const auto& part : parts) { | |
| node = &node->children[part]; | |
| } | |
| node->is_leaf = true; | |
| } | |
| SimpleTrie::SimpleTrie(const absl::flat_hash_set<std::string>& subtrees) | |
| : subtrees_(subtrees) { | |
| for (const auto& subtree : subtrees) { | |
| AddSubtree(subtree); | |
| } | |
| } | |
| void SimpleTrie::DebugPrint() const { | |
| std::cerr << "tlBMC owned subtrees: \n"; | |
| for (const auto& subtree : subtrees_) { | |
| std::cerr << subtree << "\n"; | |
| } | |
| } | |
| bool SimpleTrie::IsUrlInSubtrees(std::string_view url) const { | |
| const Node* node = &root_; | |
| std::vector<std::string_view> parts = | |
| absl::StrSplit(url, '/', absl::SkipEmpty()); | |
| for (const auto& part : parts) { | |
| if (node->is_leaf) { | |
| return true; | |
| } | |
| auto it = node->children.find(part); | |
| if (it == node->children.end()) { | |
| return false; | |
| } | |
| node = &it->second; | |
| } | |
| return node->is_leaf; | |
| } | |
| } // namespace internal | |
| SmartRouter::SmartRouter(bool allow_session_empty, AppInterface* tlbmc_app) | |
| : gbmcweb_router_(allow_session_empty), | |
| tlbmc_plugin_router_(allow_session_empty), | |
| tlbmc_app_(tlbmc_app), | |
| tlbmc_owned_urls_(GetTlbmcOwnedUrls()), | |
| tlbmc_owned_subtrees_(GetTlbmcOwnedSubtrees()) { | |
| // We still need this logic to set the smart router for the tlbmc router. | |
| if (tlbmc_app_ != nullptr) { | |
| tlbmc_app_->SetSmartRouter(this); | |
| std::cerr << "SmartRouter created! Owned URLs: \n"; | |
| for (const auto& [url, methods] : tlbmc_owned_urls_) { | |
| std::cerr << "url: " << url | |
| << " methods: " << milotic_tlbmc::MethodFieldsToString(methods) | |
| << "\n"; | |
| } | |
| } | |
| } | |
| void SmartRouter::Handle(crow::Request& req, | |
| const std::shared_ptr<bmcweb::AsyncResp>& async_resp) { | |
| boost::urls::url url(req.target()); | |
| // If the request is not owned by TLBMC, then we will forward it to gBMCWeb. | |
| if (tlbmc_app_ == nullptr || | |
| !IsTlbmcOwnedUrl(url.encoded_path(), req.method())) { | |
| gbmcweb_router_.handle(req, async_resp); | |
| if (tlbmc_app_ != nullptr) { | |
| milotic_tlbmc::StaticSharedMemoryServer::GetInstance() | |
| .UpdateMetricsRequestCount(false); | |
| } | |
| return; | |
| } | |
| // Otherwise we prepare for tlbmc to handle it. | |
| milotic_tlbmc::StaticSharedMemoryServer::GetInstance() | |
| .UpdateMetricsRequestCount(true); | |
| tlbmc_app_->HandleFromCrowRequest(req, async_resp); | |
| Router::FindRouteResponse found_route = tlbmc_plugin_router_.findRoute(req); | |
| if (found_route.route.rule != nullptr) { | |
| found_route.route.rule->handle(req, async_resp, found_route.route.params); | |
| } | |
| } | |
| std::vector<const std::string*> SmartRouter::GetRoutes( | |
| const std::string& parent) { | |
| return gbmcweb_router_.getRoutes(parent); | |
| } | |
| DynamicRule& SmartRouter::NewRuleDynamic(std::string&& rule) { | |
| return gbmcweb_router_.newRuleDynamic(rule); | |
| } | |
| void SmartRouter::DebugPrint() { | |
| gbmcweb_router_.debugPrint(); | |
| if (tlbmc_app_ != nullptr) { | |
| tlbmc_app_->DebugPrint(); | |
| } | |
| } | |
| void SmartRouter::Validate() { | |
| gbmcweb_router_.validate(); | |
| tlbmc_plugin_router_.validate(); | |
| } | |
| absl::flat_hash_map<std::string, size_t> SmartRouter::GetTlbmcOwnedUrls() { | |
| if (tlbmc_app_ == nullptr) { | |
| return {}; | |
| } | |
| return tlbmc_app_->GetOwnedUrls(); | |
| } | |
| absl::flat_hash_set<std::string> SmartRouter::GetTlbmcOwnedSubtrees() { | |
| if (tlbmc_app_ == nullptr) { | |
| return {}; | |
| } | |
| return tlbmc_app_->GetOwnedSubtrees(); | |
| } | |
| bool SmartRouter::IsTlbmcOwnedUrl(std::string_view url, | |
| boost::beast::http::verb verb) { | |
| // Our `tlbmc_owned_urls_` do not have a trailing slash | |
| if (url.ends_with('/')) { | |
| url.remove_suffix(1); | |
| } | |
| // NOLINTNEXTLINE: Yocto's Abseil is old and only supports pointer type | |
| absl::MutexLock lock(tlbmc_owned_urls_subtrees_mutex_); | |
| if (tlbmc_owned_subtrees_.IsUrlInSubtrees(url)) { | |
| return true; | |
| } | |
| auto it = tlbmc_owned_urls_.find(url); | |
| if (it == tlbmc_owned_urls_.end()) { | |
| return false; | |
| } | |
| return milotic_tlbmc::MethodFieldsContainBoostVerb(it->second, verb); | |
| } | |
| void SmartRouter::UpdateTlbmcOwnedUrls() { | |
| // NOLINTNEXTLINE: Yocto's Abseil is old and only supports pointer type | |
| absl::MutexLock lock(tlbmc_owned_urls_subtrees_mutex_); | |
| tlbmc_owned_urls_ = GetTlbmcOwnedUrls(); | |
| tlbmc_owned_subtrees_ = internal::SimpleTrie(GetTlbmcOwnedSubtrees()); | |
| } | |
| } // namespace crow |