aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/hub/httphubservice.cpp
diff options
context:
space:
mode:
authorDan Engelbrecht <[email protected]>2026-03-11 09:45:31 +0100
committerGitHub Enterprise <[email protected]>2026-03-11 09:45:31 +0100
commite9d04250225a430cffed28e6a49299e3da542f97 (patch)
tree7f99594bd930c072e159458319e822bbb18794f7 /src/zenserver/hub/httphubservice.cpp
parentminor zenstore/blockstore fixes (#821) (diff)
downloadzen-e9d04250225a430cffed28e6a49299e3da542f97.tar.xz
zen-e9d04250225a430cffed28e6a49299e3da542f97.zip
hub consul integration (#820)
- Feature: Basic consul integration for zenserver hub mode, restricted to host local consul agent and register/deregister of services - Feature: Added new options to zenserver hub mode - `consul-endpoint` - Consul endpoint URL for service registration (empty = disabled) - `hub-base-port-number` - Base port number for provisioned instances - `hub-instance-limit` - Maximum number of provisioned instances for this hub - `hub-use-job-object` - Enable the use of a Windows Job Object for child process management (Windows only)
Diffstat (limited to 'src/zenserver/hub/httphubservice.cpp')
-rw-r--r--src/zenserver/hub/httphubservice.cpp213
1 files changed, 213 insertions, 0 deletions
diff --git a/src/zenserver/hub/httphubservice.cpp b/src/zenserver/hub/httphubservice.cpp
new file mode 100644
index 000000000..67ed0cfd8
--- /dev/null
+++ b/src/zenserver/hub/httphubservice.cpp
@@ -0,0 +1,213 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include "httphubservice.h"
+
+#include "hub.h"
+#include "storageserverinstance.h"
+
+#include <zencore/compactbinarybuilder.h>
+#include <zencore/fmtutils.h>
+#include <zencore/logging.h>
+
+namespace zen {
+
+HttpHubService::HttpHubService(Hub& Hub) : m_Hub(Hub)
+{
+ using namespace std::literals;
+
+ m_Router.AddMatcher("moduleid", [](std::string_view Str) -> bool {
+ for (const auto C : Str)
+ {
+ if (std::isalnum(C) || C == '-')
+ {
+ // fine
+ }
+ else
+ {
+ // not fine
+ return false;
+ }
+ }
+
+ return true;
+ });
+
+ m_Router.RegisterRoute(
+ "status",
+ [this](HttpRouterRequest& Req) {
+ CbObjectWriter Obj;
+ Obj.BeginArray("modules");
+ m_Hub.EnumerateModules([&Obj](StorageServerInstance& Instance) {
+ Obj.BeginObject();
+ Obj << "moduleId" << Instance.GetModuleId();
+ Obj << "provisioned" << Instance.IsProvisioned();
+ Obj.EndObject();
+ });
+ Obj.EndArray();
+ Req.ServerRequest().WriteResponse(HttpResponseCode::OK, Obj.Save());
+ },
+ HttpVerb::kGet);
+
+ m_Router.RegisterRoute(
+ "modules/{moduleid}",
+ [this](HttpRouterRequest& Req) {
+ std::string_view ModuleId = Req.GetCapture(1);
+
+ if (Req.ServerRequest().RequestVerb() == HttpVerb::kDelete)
+ {
+ HandleModuleDelete(Req.ServerRequest(), ModuleId);
+ }
+ else
+ {
+ HandleModuleGet(Req.ServerRequest(), ModuleId);
+ }
+ },
+ HttpVerb::kGet | HttpVerb::kDelete);
+
+ m_Router.RegisterRoute(
+ "modules/{moduleid}/provision",
+ [this](HttpRouterRequest& Req) {
+ std::string_view ModuleId = Req.GetCapture(1);
+
+ std::string FailureReason = "unknown";
+ HttpResponseCode ResponseCode = HttpResponseCode::OK;
+
+ try
+ {
+ HubProvisionedInstanceInfo Info;
+ if (m_Hub.Provision(ModuleId, /* out */ Info, /* out */ FailureReason))
+ {
+ CbObjectWriter Obj;
+ Obj << "moduleId" << ModuleId;
+ Obj << "baseUri" << Info.BaseUri;
+ Obj << "port" << Info.Port;
+ Req.ServerRequest().WriteResponse(HttpResponseCode::OK, Obj.Save());
+
+ return;
+ }
+ else
+ {
+ ResponseCode = HttpResponseCode::BadRequest;
+ }
+ }
+ catch (const std::exception& Ex)
+ {
+ ZEN_ERROR("Exception while provisioning module '{}': {}", ModuleId, Ex.what());
+
+ FailureReason = Ex.what();
+ ResponseCode = HttpResponseCode::InternalServerError;
+ }
+
+ Req.ServerRequest().WriteResponse(ResponseCode, HttpContentType::kText, FailureReason);
+ },
+ HttpVerb::kPost);
+
+ m_Router.RegisterRoute(
+ "modules/{moduleid}/deprovision",
+ [this](HttpRouterRequest& Req) {
+ std::string_view ModuleId = Req.GetCapture(1);
+ std::string FailureReason = "unknown";
+
+ try
+ {
+ if (!m_Hub.Deprovision(std::string(ModuleId), /* out */ FailureReason))
+ {
+ if (FailureReason.empty())
+ {
+ return Req.ServerRequest().WriteResponse(HttpResponseCode::NotFound);
+ }
+ else
+ {
+ return Req.ServerRequest().WriteResponse(HttpResponseCode::BadRequest, HttpContentType::kText, FailureReason);
+ }
+ }
+
+ CbObjectWriter Obj;
+ Obj << "moduleId" << ModuleId;
+
+ return Req.ServerRequest().WriteResponse(HttpResponseCode::OK, Obj.Save());
+ }
+ catch (const std::exception& Ex)
+ {
+ ZEN_ERROR("Exception while deprovisioning module '{}': {}", ModuleId, Ex.what());
+
+ FailureReason = Ex.what();
+ }
+
+ Req.ServerRequest().WriteResponse(HttpResponseCode::InternalServerError, HttpContentType::kText, FailureReason);
+ },
+ HttpVerb::kPost);
+
+ m_Router.RegisterRoute(
+ "stats",
+ [this](HttpRouterRequest& Req) {
+ CbObjectWriter Obj;
+ Obj << "currentInstanceCount" << m_Hub.GetInstanceCount();
+ Obj << "maxInstanceCount" << m_Hub.GetMaxInstanceCount();
+ Obj << "instanceLimit" << m_Hub.GetConfig().InstanceLimit;
+ Req.ServerRequest().WriteResponse(HttpResponseCode::OK, Obj.Save());
+ },
+ HttpVerb::kGet);
+}
+
+HttpHubService::~HttpHubService()
+{
+}
+
+const char*
+HttpHubService::BaseUri() const
+{
+ return "/hub/";
+}
+
+void
+HttpHubService::SetNotificationEndpoint(std::string_view UpstreamNotificationEndpoint, std::string_view InstanceId)
+{
+ ZEN_UNUSED(UpstreamNotificationEndpoint, InstanceId);
+ // TODO: store these for use in notifications, on some interval/criteria which is currently TBD
+}
+
+void
+HttpHubService::HandleRequest(zen::HttpServerRequest& Request)
+{
+ m_Router.HandleRequest(Request);
+}
+
+void
+HttpHubService::HandleModuleGet(HttpServerRequest& Request, std::string_view ModuleId)
+{
+ StorageServerInstance* Instance = nullptr;
+ if (!m_Hub.Find(ModuleId, &Instance))
+ {
+ Request.WriteResponse(HttpResponseCode::NotFound);
+ return;
+ }
+
+ // TODO: A separate http request for the modules/{moduleid}/deprovision endpoint can be called and deprovision the instance leaving us
+ // with a dangling pointer...
+
+ CbObjectWriter Obj;
+ Obj << "moduleId" << Instance->GetModuleId();
+ Obj << "provisioned" << Instance->IsProvisioned();
+ Request.WriteResponse(HttpResponseCode::OK, Obj.Save());
+}
+
+void
+HttpHubService::HandleModuleDelete(HttpServerRequest& Request, std::string_view ModuleId)
+{
+ StorageServerInstance* Instance = nullptr;
+ if (!m_Hub.Find(ModuleId, &Instance))
+ {
+ Request.WriteResponse(HttpResponseCode::NotFound);
+ return;
+ }
+
+ // TODO: deprovision and nuke all related storage
+
+ CbObjectWriter Obj;
+ Obj << "moduleId" << Instance->GetModuleId();
+ Obj << "provisioned" << Instance->IsProvisioned();
+ Request.WriteResponse(HttpResponseCode::OK, Obj.Save());
+}
+
+} // namespace zen