diff options
| author | Stefan Boberg <[email protected]> | 2025-12-11 09:34:24 +0100 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2025-12-11 09:34:24 +0100 |
| commit | 3de9a65cd990f2a4f5395b7e2a094471633eb98b (patch) | |
| tree | f1640a32fd2b68a8f1b6f77f5ba5c4cbf959cb0b /src | |
| parent | 5.7.14-pre3 (diff) | |
| download | zen-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.cpp | 43 | ||||
| -rw-r--r-- | src/zenhttp/include/zenhttp/httpapiservice.h | 21 | ||||
| -rw-r--r-- | src/zenhttp/include/zenhttp/httpserver.h | 21 | ||||
| -rw-r--r-- | src/zenhttp/monitoring/httpapiservice.cpp | 47 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpasio.cpp | 20 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpmulti.cpp | 10 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpmulti.h | 10 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpnull.cpp | 10 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpnull.h | 10 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpplugin.cpp | 20 | ||||
| -rw-r--r-- | src/zenhttp/servers/httpsys.cpp | 20 | ||||
| -rw-r--r-- | src/zenserver-test/buildstore-tests.cpp | 24 |
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); } |