aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2025-12-11 09:34:24 +0100
committerGitHub Enterprise <[email protected]>2025-12-11 09:34:24 +0100
commit3de9a65cd990f2a4f5395b7e2a094471633eb98b (patch)
treef1640a32fd2b68a8f1b6f77f5ba5c4cbf959cb0b /src
parent5.7.14-pre3 (diff)
downloadzen-3de9a65cd990f2a4f5395b7e2a094471633eb98b.tar.xz
zen-3de9a65cd990f2a4f5395b7e2a094471633eb98b.zip
HTTP server API changes for improved extensibility (#684)
* refactored `HttpServer` so all subclass member functions are proctected, to make it easier to extend base functionality * added API service, can be used to enumerate registered endpoints (at `/api`). Currently only very basic information is provided
Diffstat (limited to 'src')
-rw-r--r--src/zenhttp/httpserver.cpp43
-rw-r--r--src/zenhttp/include/zenhttp/httpapiservice.h21
-rw-r--r--src/zenhttp/include/zenhttp/httpserver.h21
-rw-r--r--src/zenhttp/monitoring/httpapiservice.cpp47
-rw-r--r--src/zenhttp/servers/httpasio.cpp20
-rw-r--r--src/zenhttp/servers/httpmulti.cpp10
-rw-r--r--src/zenhttp/servers/httpmulti.h10
-rw-r--r--src/zenhttp/servers/httpnull.cpp10
-rw-r--r--src/zenhttp/servers/httpnull.h10
-rw-r--r--src/zenhttp/servers/httpplugin.cpp20
-rw-r--r--src/zenhttp/servers/httpsys.cpp20
-rw-r--r--src/zenserver-test/buildstore-tests.cpp24
12 files changed, 189 insertions, 67 deletions
diff --git a/src/zenhttp/httpserver.cpp b/src/zenhttp/httpserver.cpp
index 1357f1b9b..e529fb76e 100644
--- a/src/zenhttp/httpserver.cpp
+++ b/src/zenhttp/httpserver.cpp
@@ -990,6 +990,49 @@ HttpRequestRouter::HandleRequest(zen::HttpServerRequest& Request)
//////////////////////////////////////////////////////////////////////////
+int
+HttpServer::Initialize(int BasePort, std::filesystem::path DataDir)
+{
+ return OnInitialize(BasePort, std::move(DataDir));
+}
+
+void
+HttpServer::Run(bool IsInteractiveSession)
+{
+ OnRun(IsInteractiveSession);
+}
+
+void
+HttpServer::RequestExit()
+{
+ OnRequestExit();
+}
+void
+HttpServer::Close()
+{
+ OnClose();
+}
+
+void
+HttpServer::RegisterService(HttpService& Service)
+{
+ OnRegisterService(Service);
+ m_KnownServices.push_back(&Service);
+}
+
+void
+HttpServer::EnumerateServices(std::function<void(HttpService& Service)>&& Callback)
+{
+ // This doesn't take a lock because services should only be registered during
+ // server initialization, before it starts accepting requests
+ for (HttpService* Service : m_KnownServices)
+ {
+ Callback(*Service);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+
HttpRpcHandler::HttpRpcHandler()
{
}
diff --git a/src/zenhttp/include/zenhttp/httpapiservice.h b/src/zenhttp/include/zenhttp/httpapiservice.h
new file mode 100644
index 000000000..0270973bf
--- /dev/null
+++ b/src/zenhttp/include/zenhttp/httpapiservice.h
@@ -0,0 +1,21 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include <zenhttp/httpserver.h>
+
+namespace zen {
+
+class HttpApiService : public HttpService
+{
+public:
+ HttpApiService(HttpServer& Server);
+ ~HttpApiService() override;
+
+ const char* BaseUri() const override;
+ void HandleRequest(HttpServerRequest& HttpServiceRequest) override;
+
+private:
+ HttpServer& m_Server;
+ HttpRequestRouter m_Router;
+};
+
+} // namespace zen
diff --git a/src/zenhttp/include/zenhttp/httpserver.h b/src/zenhttp/include/zenhttp/httpserver.h
index f95ec51d2..9e5c41ab7 100644
--- a/src/zenhttp/include/zenhttp/httpserver.h
+++ b/src/zenhttp/include/zenhttp/httpserver.h
@@ -175,11 +175,22 @@ private:
class HttpServer : public RefCounted
{
public:
- virtual void RegisterService(HttpService& Service) = 0;
- virtual int Initialize(int BasePort, std::filesystem::path DataDir) = 0;
- virtual void Run(bool IsInteractiveSession) = 0;
- virtual void RequestExit() = 0;
- virtual void Close() = 0;
+ void RegisterService(HttpService& Service);
+ void EnumerateServices(std::function<void(HttpService&)>&& Callback);
+
+ int Initialize(int BasePort, std::filesystem::path DataDir);
+ void Run(bool IsInteractiveSession);
+ void RequestExit();
+ void Close();
+
+private:
+ std::vector<HttpService*> m_KnownServices;
+
+ virtual void OnRegisterService(HttpService& Service) = 0;
+ virtual int OnInitialize(int BasePort, std::filesystem::path DataDir) = 0;
+ virtual void OnRun(bool IsInteractiveSession) = 0;
+ virtual void OnRequestExit() = 0;
+ virtual void OnClose() = 0;
};
struct HttpServerPluginConfig
diff --git a/src/zenhttp/monitoring/httpapiservice.cpp b/src/zenhttp/monitoring/httpapiservice.cpp
new file mode 100644
index 000000000..f8cfa25f4
--- /dev/null
+++ b/src/zenhttp/monitoring/httpapiservice.cpp
@@ -0,0 +1,47 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include <zenhttp/httpapiservice.h>
+
+#include <zencore/compactbinarybuilder.h>
+
+namespace zen {
+
+HttpApiService::HttpApiService(HttpServer& Server) : m_Server(Server)
+{
+ m_Router.RegisterRoute(
+ "",
+ [&](HttpRouterRequest& Request) {
+ CbObjectWriter ResponseData;
+
+ ResponseData.BeginArray("services");
+
+ m_Server.EnumerateServices([&](HttpService& Service) {
+ ResponseData.BeginObject();
+ ResponseData << "base_uri" << Service.BaseUri();
+ ResponseData.EndObject();
+ });
+
+ ResponseData.EndArray();
+
+ Request.ServerRequest().WriteResponse(HttpResponseCode::OK, ResponseData.Save());
+ },
+ HttpVerb::kGet);
+}
+
+HttpApiService::~HttpApiService()
+{
+}
+
+const char*
+HttpApiService::BaseUri() const
+{
+ return "/api/";
+}
+
+void
+HttpApiService::HandleRequest(HttpServerRequest& HttpServiceRequest)
+{
+ m_Router.HandleRequest(HttpServiceRequest);
+}
+
+} // namespace zen
diff --git a/src/zenhttp/servers/httpasio.cpp b/src/zenhttp/servers/httpasio.cpp
index 3970dea12..12aee1ee0 100644
--- a/src/zenhttp/servers/httpasio.cpp
+++ b/src/zenhttp/servers/httpasio.cpp
@@ -1298,11 +1298,11 @@ public:
HttpAsioServer(const AsioConfig& Config);
~HttpAsioServer();
- virtual void RegisterService(HttpService& Service) override;
- virtual int Initialize(int BasePort, std::filesystem::path DataDir) override;
- virtual void Run(bool IsInteractiveSession) override;
- virtual void RequestExit() override;
- virtual void Close() override;
+ virtual void OnRegisterService(HttpService& Service) override;
+ virtual int OnInitialize(int BasePort, std::filesystem::path DataDir) override;
+ virtual void OnRun(bool IsInteractiveSession) override;
+ virtual void OnRequestExit() override;
+ virtual void OnClose() override;
private:
Event m_ShutdownEvent;
@@ -1328,7 +1328,7 @@ HttpAsioServer::~HttpAsioServer()
}
void
-HttpAsioServer::Close()
+HttpAsioServer::OnClose()
{
try
{
@@ -1342,13 +1342,13 @@ HttpAsioServer::Close()
}
void
-HttpAsioServer::RegisterService(HttpService& Service)
+HttpAsioServer::OnRegisterService(HttpService& Service)
{
m_Impl->RegisterService(Service.BaseUri(), Service);
}
int
-HttpAsioServer::Initialize(int BasePort, std::filesystem::path DataDir)
+HttpAsioServer::OnInitialize(int BasePort, std::filesystem::path DataDir)
{
ZEN_TRACE_CPU("HttpAsioServer::Initialize");
m_Impl->Initialize(DataDir);
@@ -1372,7 +1372,7 @@ HttpAsioServer::Initialize(int BasePort, std::filesystem::path DataDir)
}
void
-HttpAsioServer::Run(bool IsInteractive)
+HttpAsioServer::OnRun(bool IsInteractive)
{
const bool TestMode = !IsInteractive;
@@ -1416,7 +1416,7 @@ HttpAsioServer::Run(bool IsInteractive)
}
void
-HttpAsioServer::RequestExit()
+HttpAsioServer::OnRequestExit()
{
m_ShutdownEvent.Set();
}
diff --git a/src/zenhttp/servers/httpmulti.cpp b/src/zenhttp/servers/httpmulti.cpp
index b8b7931a9..6541a1c48 100644
--- a/src/zenhttp/servers/httpmulti.cpp
+++ b/src/zenhttp/servers/httpmulti.cpp
@@ -20,7 +20,7 @@ HttpMultiServer::~HttpMultiServer()
}
void
-HttpMultiServer::RegisterService(HttpService& Service)
+HttpMultiServer::OnRegisterService(HttpService& Service)
{
for (auto& Server : m_Servers)
{
@@ -29,7 +29,7 @@ HttpMultiServer::RegisterService(HttpService& Service)
}
int
-HttpMultiServer::Initialize(int BasePort, std::filesystem::path DataDir)
+HttpMultiServer::OnInitialize(int BasePort, std::filesystem::path DataDir)
{
ZEN_TRACE_CPU("HttpMultiServer::Initialize");
@@ -54,7 +54,7 @@ HttpMultiServer::Initialize(int BasePort, std::filesystem::path DataDir)
}
void
-HttpMultiServer::Run(bool IsInteractiveSession)
+HttpMultiServer::OnRun(bool IsInteractiveSession)
{
const bool TestMode = !IsInteractiveSession;
@@ -98,13 +98,13 @@ HttpMultiServer::Run(bool IsInteractiveSession)
}
void
-HttpMultiServer::RequestExit()
+HttpMultiServer::OnRequestExit()
{
m_ShutdownEvent.Set();
}
void
-HttpMultiServer::Close()
+HttpMultiServer::OnClose()
{
for (auto& Server : m_Servers)
{
diff --git a/src/zenhttp/servers/httpmulti.h b/src/zenhttp/servers/httpmulti.h
index 53cf57568..ae0ed74cf 100644
--- a/src/zenhttp/servers/httpmulti.h
+++ b/src/zenhttp/servers/httpmulti.h
@@ -15,11 +15,11 @@ public:
HttpMultiServer();
~HttpMultiServer();
- virtual void RegisterService(HttpService& Service) override;
- virtual int Initialize(int BasePort, std::filesystem::path DataDir) override;
- virtual void Run(bool IsInteractiveSession) override;
- virtual void RequestExit() override;
- virtual void Close() override;
+ virtual void OnRegisterService(HttpService& Service) override;
+ virtual int OnInitialize(int BasePort, std::filesystem::path DataDir) override;
+ virtual void OnRun(bool IsInteractiveSession) override;
+ virtual void OnRequestExit() override;
+ virtual void OnClose() override;
void AddServer(Ref<HttpServer> Server);
diff --git a/src/zenhttp/servers/httpnull.cpp b/src/zenhttp/servers/httpnull.cpp
index 9ac1c61ce..06838a0ed 100644
--- a/src/zenhttp/servers/httpnull.cpp
+++ b/src/zenhttp/servers/httpnull.cpp
@@ -19,20 +19,20 @@ HttpNullServer::~HttpNullServer()
}
void
-HttpNullServer::RegisterService(HttpService& Service)
+HttpNullServer::OnRegisterService(HttpService& Service)
{
ZEN_UNUSED(Service);
}
int
-HttpNullServer::Initialize(int BasePort, std::filesystem::path DataDir)
+HttpNullServer::OnInitialize(int BasePort, std::filesystem::path DataDir)
{
ZEN_UNUSED(DataDir);
return BasePort;
}
void
-HttpNullServer::Run(bool IsInteractiveSession)
+HttpNullServer::OnRun(bool IsInteractiveSession)
{
const bool TestMode = !IsInteractiveSession;
@@ -76,13 +76,13 @@ HttpNullServer::Run(bool IsInteractiveSession)
}
void
-HttpNullServer::RequestExit()
+HttpNullServer::OnRequestExit()
{
m_ShutdownEvent.Set();
}
void
-HttpNullServer::Close()
+HttpNullServer::OnClose()
{
}
diff --git a/src/zenhttp/servers/httpnull.h b/src/zenhttp/servers/httpnull.h
index 818020604..ce7230938 100644
--- a/src/zenhttp/servers/httpnull.h
+++ b/src/zenhttp/servers/httpnull.h
@@ -17,11 +17,11 @@ public:
HttpNullServer();
~HttpNullServer();
- virtual void RegisterService(HttpService& Service) override;
- virtual int Initialize(int BasePort, std::filesystem::path DataDir) override;
- virtual void Run(bool IsInteractiveSession) override;
- virtual void RequestExit() override;
- virtual void Close() override;
+ virtual void OnRegisterService(HttpService& Service) override;
+ virtual int OnInitialize(int BasePort, std::filesystem::path DataDir) override;
+ virtual void OnRun(bool IsInteractiveSession) override;
+ virtual void OnRequestExit() override;
+ virtual void OnClose() override;
private:
Event m_ShutdownEvent;
diff --git a/src/zenhttp/servers/httpplugin.cpp b/src/zenhttp/servers/httpplugin.cpp
index d6ca7e1c5..426e62179 100644
--- a/src/zenhttp/servers/httpplugin.cpp
+++ b/src/zenhttp/servers/httpplugin.cpp
@@ -95,11 +95,11 @@ struct HttpPluginServerImpl : public HttpPluginServer, TransportServer
// HttpPluginServer
- virtual void RegisterService(HttpService& Service) override;
- virtual int Initialize(int BasePort, std::filesystem::path DataDir) override;
- virtual void Run(bool IsInteractiveSession) override;
- virtual void RequestExit() override;
- virtual void Close() override;
+ virtual void OnRegisterService(HttpService& Service) override;
+ virtual int OnInitialize(int BasePort, std::filesystem::path DataDir) override;
+ virtual void OnRun(bool IsInteractiveSession) override;
+ virtual void OnRequestExit() override;
+ virtual void OnClose() override;
virtual void AddPlugin(Ref<TransportPlugin> Plugin) override;
virtual void RemovePlugin(Ref<TransportPlugin> Plugin) override;
@@ -706,7 +706,7 @@ HttpPluginServerImpl::CreateConnectionHandler(TransportConnection* Connection)
}
int
-HttpPluginServerImpl::Initialize(int BasePort, std::filesystem::path DataDir)
+HttpPluginServerImpl::OnInitialize(int BasePort, std::filesystem::path DataDir)
{
ZEN_TRACE_CPU("HttpPluginServerImpl::Initialize");
@@ -740,7 +740,7 @@ HttpPluginServerImpl::Initialize(int BasePort, std::filesystem::path DataDir)
}
void
-HttpPluginServerImpl::Close()
+HttpPluginServerImpl::OnClose()
{
if (!m_IsInitialized)
return;
@@ -776,7 +776,7 @@ HttpPluginServerImpl::Close()
}
void
-HttpPluginServerImpl::Run(bool IsInteractive)
+HttpPluginServerImpl::OnRun(bool IsInteractive)
{
ZEN_MEMSCOPE(GetHttppluginTag());
@@ -822,7 +822,7 @@ HttpPluginServerImpl::Run(bool IsInteractive)
}
void
-HttpPluginServerImpl::RequestExit()
+HttpPluginServerImpl::OnRequestExit()
{
m_ShutdownEvent.Set();
}
@@ -850,7 +850,7 @@ HttpPluginServerImpl::RemovePlugin(Ref<TransportPlugin> Plugin)
}
void
-HttpPluginServerImpl::RegisterService(HttpService& Service)
+HttpPluginServerImpl::OnRegisterService(HttpService& Service)
{
ZEN_MEMSCOPE(GetHttppluginTag());
diff --git a/src/zenhttp/servers/httpsys.cpp b/src/zenhttp/servers/httpsys.cpp
index 9dbdd7167..7b02f95f1 100644
--- a/src/zenhttp/servers/httpsys.cpp
+++ b/src/zenhttp/servers/httpsys.cpp
@@ -54,11 +54,11 @@ public:
// HttpServer interface implementation
- virtual int Initialize(int BasePort, std::filesystem::path DataDir) override;
- virtual void Run(bool TestMode) override;
- virtual void RequestExit() override;
- virtual void RegisterService(HttpService& Service) override;
- virtual void Close() override;
+ virtual int OnInitialize(int BasePort, std::filesystem::path DataDir) override;
+ virtual void OnRun(bool TestMode) override;
+ virtual void OnRequestExit() override;
+ virtual void OnRegisterService(HttpService& Service) override;
+ virtual void OnClose() override;
WorkerThreadPool& WorkPool();
@@ -984,7 +984,7 @@ HttpSysServer::~HttpSysServer()
}
void
-HttpSysServer::Close()
+HttpSysServer::OnClose()
{
if (m_IsHttpInitialized)
{
@@ -1292,7 +1292,7 @@ HttpSysServer::StartServer()
}
void
-HttpSysServer::Run(bool IsInteractive)
+HttpSysServer::OnRun(bool IsInteractive)
{
if (IsInteractive)
{
@@ -2097,7 +2097,7 @@ InitialRequestHandler::HandleCompletion(ULONG IoResult, ULONG_PTR NumberOfBytesT
//
int
-HttpSysServer::Initialize(int BasePort, std::filesystem::path DataDir)
+HttpSysServer::OnInitialize(int BasePort, std::filesystem::path DataDir)
{
ZEN_TRACE_CPU("HttpSysServer::Initialize");
@@ -2115,13 +2115,13 @@ HttpSysServer::Initialize(int BasePort, std::filesystem::path DataDir)
}
void
-HttpSysServer::RequestExit()
+HttpSysServer::OnRequestExit()
{
m_ShutdownEvent.Set();
}
void
-HttpSysServer::RegisterService(HttpService& Service)
+HttpSysServer::OnRegisterService(HttpService& Service)
{
RegisterService(Service.BaseUri(), Service);
}
diff --git a/src/zenserver-test/buildstore-tests.cpp b/src/zenserver-test/buildstore-tests.cpp
index 29afd3f9d..ec6f17d97 100644
--- a/src/zenserver-test/buildstore-tests.cpp
+++ b/src/zenserver-test/buildstore-tests.cpp
@@ -63,15 +63,15 @@ TEST_CASE("buildstore.blobs")
{
HttpClient::Response Result = Client.Get(fmt::format("{}/{}/{}/blobs/{}", Namespace, Bucket, BuildId, RawHash),
HttpClient::Accept(ZenContentType::kCompressedBinary));
- CHECK(Result);
+ REQUIRE(Result);
IoBuffer Payload = Result.ResponsePayload;
- CHECK(Payload.GetContentType() == ZenContentType::kCompressedBinary);
+ REQUIRE(Payload.GetContentType() == ZenContentType::kCompressedBinary);
IoHash VerifyRawHash;
uint64_t VerifyRawSize;
CompressedBuffer CompressedBlob =
CompressedBuffer::FromCompressed(SharedBuffer(std::move(Payload)), VerifyRawHash, VerifyRawSize);
- CHECK(CompressedBlob);
- CHECK(VerifyRawHash == RawHash);
+ REQUIRE(CompressedBlob);
+ REQUIRE(VerifyRawHash == RawHash);
IoBuffer Decompressed = CompressedBlob.Decompress().AsIoBuffer();
CHECK(IoHash::HashBuffer(Decompressed) == RawHash);
}
@@ -89,15 +89,15 @@ TEST_CASE("buildstore.blobs")
{
HttpClient::Response Result = Client.Get(fmt::format("{}/{}/{}/blobs/{}", Namespace, Bucket, BuildId, RawHash),
HttpClient::Accept(ZenContentType::kCompressedBinary));
- CHECK(Result);
+ REQUIRE(Result);
IoBuffer Payload = Result.ResponsePayload;
- CHECK(Payload.GetContentType() == ZenContentType::kCompressedBinary);
+ REQUIRE(Payload.GetContentType() == ZenContentType::kCompressedBinary);
IoHash VerifyRawHash;
uint64_t VerifyRawSize;
CompressedBuffer CompressedBlob =
CompressedBuffer::FromCompressed(SharedBuffer(std::move(Payload)), VerifyRawHash, VerifyRawSize);
- CHECK(CompressedBlob);
- CHECK(VerifyRawHash == RawHash);
+ REQUIRE(CompressedBlob);
+ REQUIRE(VerifyRawHash == RawHash);
IoBuffer Decompressed = CompressedBlob.Decompress().AsIoBuffer();
CHECK(IoHash::HashBuffer(Decompressed) == RawHash);
}
@@ -128,15 +128,15 @@ TEST_CASE("buildstore.blobs")
{
HttpClient::Response Result = Client.Get(fmt::format("{}/{}/{}/blobs/{}", Namespace, Bucket, BuildId, RawHash),
HttpClient::Accept(ZenContentType::kCompressedBinary));
- CHECK(Result);
+ REQUIRE(Result);
IoBuffer Payload = Result.ResponsePayload;
- CHECK(Payload.GetContentType() == ZenContentType::kCompressedBinary);
+ REQUIRE(Payload.GetContentType() == ZenContentType::kCompressedBinary);
IoHash VerifyRawHash;
uint64_t VerifyRawSize;
CompressedBuffer CompressedBlob =
CompressedBuffer::FromCompressed(SharedBuffer(std::move(Payload)), VerifyRawHash, VerifyRawSize);
- CHECK(CompressedBlob);
- CHECK(VerifyRawHash == RawHash);
+ REQUIRE(CompressedBlob);
+ REQUIRE(VerifyRawHash == RawHash);
IoBuffer Decompressed = CompressedBlob.Decompress().AsIoBuffer();
CHECK(IoHash::HashBuffer(Decompressed) == RawHash);
}