aboutsummaryrefslogtreecommitdiff
path: root/src/zenhttp/include
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2026-03-09 17:43:08 +0100
committerGitHub Enterprise <[email protected]>2026-03-09 17:43:08 +0100
commitb37b34ea6ad906f54e8104526e77ba66aed997da (patch)
treee80ce17d666aff6d2f0d73d4977128ffb4055476 /src/zenhttp/include
parentadd fallback for zencache multirange (#816) (diff)
downloadzen-b37b34ea6ad906f54e8104526e77ba66aed997da.tar.xz
zen-b37b34ea6ad906f54e8104526e77ba66aed997da.zip
Dashboard overhaul, compute integration (#814)
- **Frontend dashboard overhaul**: Unified compute/main dashboards into a single shared UI. Added new pages for cache, projects, metrics, sessions, info (build/runtime config, system stats). Added live-update via WebSockets with pause control, sortable detail tables, themed styling. Refactored compute/hub/orchestrator pages into modular JS. - **HTTP server fixes and stats**: Fixed http.sys local-only fallback when default port is in use, implemented root endpoint redirect for http.sys, fixed Linux/Mac port reuse. Added /stats endpoint exposing HTTP server metrics (bytes transferred, request rates). Added WebSocket stats tracking. - **OTEL/diagnostics hardening**: Improved OTLP HTTP exporter with better error handling and resilience. Extended diagnostics services configuration. - **Session management**: Added new sessions service with HTTP endpoints for registering, updating, querying, and removing sessions. Includes session log file support. This is still WIP. - **CLI subcommand support**: Added support for commands with subcommands in the zen CLI tool, with improved command dispatch. - **Misc**: Exposed CPU usage/hostname to frontend, fixed JS compact binary float32/float64 decoding, limited projects displayed on front page to 25 sorted by last access, added vscode:// link support. Also contains some fixes from TSAN analysis.
Diffstat (limited to 'src/zenhttp/include')
-rw-r--r--src/zenhttp/include/zenhttp/httpclient.h9
-rw-r--r--src/zenhttp/include/zenhttp/httpserver.h73
-rw-r--r--src/zenhttp/include/zenhttp/httpstats.h47
3 files changed, 115 insertions, 14 deletions
diff --git a/src/zenhttp/include/zenhttp/httpclient.h b/src/zenhttp/include/zenhttp/httpclient.h
index bec4984db..1bb36a298 100644
--- a/src/zenhttp/include/zenhttp/httpclient.h
+++ b/src/zenhttp/include/zenhttp/httpclient.h
@@ -118,6 +118,15 @@ private:
class HttpClientBase;
+/** HTTP Client
+ *
+ * This is safe for use on multiple threads simultaneously, as each
+ * instance maintains an internal connection pool and will synchronize
+ * access to it as needed.
+ *
+ * Uses libcurl under the hood. We currently only use HTTP 1.1 features.
+ *
+ */
class HttpClient
{
public:
diff --git a/src/zenhttp/include/zenhttp/httpserver.h b/src/zenhttp/include/zenhttp/httpserver.h
index c1152dc3e..0e1714669 100644
--- a/src/zenhttp/include/zenhttp/httpserver.h
+++ b/src/zenhttp/include/zenhttp/httpserver.h
@@ -13,6 +13,8 @@
#include <zencore/uid.h>
#include <zenhttp/httpcommon.h>
+#include <zentelemetry/stats.h>
+
#include <functional>
#include <gsl/gsl-lite.hpp>
#include <list>
@@ -203,12 +205,34 @@ private:
int m_UriPrefixLength = 0;
};
+struct IHttpStatsProvider
+{
+ /** Handle an HTTP stats request, writing the response directly.
+ * Implementations may inspect query parameters on the request
+ * to include optional detailed breakdowns.
+ */
+ virtual void HandleStatsRequest(HttpServerRequest& Request) = 0;
+
+ /** Return the provider's current stats as a CbObject snapshot.
+ * Used by the WebSocket push thread to broadcast live updates
+ * without requiring an HttpServerRequest. Providers that do
+ * not override this will be skipped in WebSocket broadcasts.
+ */
+ virtual CbObject CollectStats() { return {}; }
+};
+
+struct IHttpStatsService
+{
+ virtual void RegisterHandler(std::string_view Id, IHttpStatsProvider& Provider) = 0;
+ virtual void UnregisterHandler(std::string_view Id, IHttpStatsProvider& Provider) = 0;
+};
+
/** HTTP server
*
* Implements the main event loop to service HTTP requests, and handles routing
* requests to the appropriate handler as registered via RegisterService
*/
-class HttpServer : public RefCounted
+class HttpServer : public RefCounted, public IHttpStatsProvider
{
public:
void RegisterService(HttpService& Service);
@@ -235,10 +259,46 @@ public:
virtual uint64_t GetTotalBytesReceived() const { return 0; }
virtual uint64_t GetTotalBytesSent() const { return 0; }
+ /** Mark that a request has been handled. Called by server implementations. */
+ void MarkRequest() { m_RequestMeter.Mark(); }
+
+ /** Set a default redirect path for root requests */
+ void SetDefaultRedirect(std::string_view Path) { m_DefaultRedirect = Path; }
+
+ std::string_view GetDefaultRedirect() const { return m_DefaultRedirect; }
+
+ /** Track active WebSocket connections — called by server implementations on upgrade/close. */
+ void OnWebSocketConnectionOpened() { m_ActiveWebSocketConnections.fetch_add(1, std::memory_order_relaxed); }
+ void OnWebSocketConnectionClosed() { m_ActiveWebSocketConnections.fetch_sub(1, std::memory_order_relaxed); }
+ uint64_t GetActiveWebSocketConnectionCount() const { return m_ActiveWebSocketConnections.load(std::memory_order_relaxed); }
+
+ /** Track WebSocket frame and byte counters — called by WS connection implementations per frame. */
+ void OnWebSocketFrameReceived(uint64_t Bytes)
+ {
+ m_WsFramesReceived.fetch_add(1, std::memory_order_relaxed);
+ m_WsBytesReceived.fetch_add(Bytes, std::memory_order_relaxed);
+ }
+ void OnWebSocketFrameSent(uint64_t Bytes)
+ {
+ m_WsFramesSent.fetch_add(1, std::memory_order_relaxed);
+ m_WsBytesSent.fetch_add(Bytes, std::memory_order_relaxed);
+ }
+
+ // IHttpStatsProvider
+ virtual CbObject CollectStats() override;
+ virtual void HandleStatsRequest(HttpServerRequest& Request) override;
+
private:
std::vector<HttpService*> m_KnownServices;
int m_EffectivePort = 0;
std::string m_ExternalHost;
+ metrics::Meter m_RequestMeter;
+ std::string m_DefaultRedirect;
+ std::atomic<uint64_t> m_ActiveWebSocketConnections{0};
+ std::atomic<uint64_t> m_WsFramesReceived{0};
+ std::atomic<uint64_t> m_WsFramesSent{0};
+ std::atomic<uint64_t> m_WsBytesReceived{0};
+ std::atomic<uint64_t> m_WsBytesSent{0};
virtual void OnRegisterService(HttpService& Service) = 0;
virtual int OnInitialize(int BasePort, std::filesystem::path DataDir) = 0;
@@ -456,17 +516,6 @@ private:
bool HandlePackageOffers(HttpService& Service, HttpServerRequest& Request, Ref<IHttpPackageHandler>& PackageHandlerRef);
-struct IHttpStatsProvider
-{
- virtual void HandleStatsRequest(HttpServerRequest& Request) = 0;
-};
-
-struct IHttpStatsService
-{
- virtual void RegisterHandler(std::string_view Id, IHttpStatsProvider& Provider) = 0;
- virtual void UnregisterHandler(std::string_view Id, IHttpStatsProvider& Provider) = 0;
-};
-
void http_forcelink(); // internal
void websocket_forcelink(); // internal
diff --git a/src/zenhttp/include/zenhttp/httpstats.h b/src/zenhttp/include/zenhttp/httpstats.h
index e6fea6765..460315faf 100644
--- a/src/zenhttp/include/zenhttp/httpstats.h
+++ b/src/zenhttp/include/zenhttp/httpstats.h
@@ -3,23 +3,50 @@
#pragma once
#include <zencore/logging.h>
+#include <zencore/thread.h>
#include <zenhttp/httpserver.h>
+#include <zenhttp/websocket.h>
+#include <atomic>
#include <map>
+#include <memory>
+#include <thread>
+#include <vector>
+
+ZEN_THIRD_PARTY_INCLUDES_START
+#include <asio/io_context.hpp>
+#include <asio/steady_timer.hpp>
+ZEN_THIRD_PARTY_INCLUDES_END
namespace zen {
-class HttpStatsService : public HttpService, public IHttpStatsService
+class HttpStatsService : public HttpService, public IHttpStatsService, public IWebSocketHandler
{
public:
- HttpStatsService();
+ /// Construct without an io_context — optionally uses a dedicated push thread
+ /// for WebSocket stats broadcasting.
+ explicit HttpStatsService(bool EnableWebSockets = false);
+
+ /// Construct with an external io_context — uses an asio timer instead
+ /// of a dedicated thread for WebSocket stats broadcasting.
+ /// The caller must ensure the io_context outlives this service and that
+ /// its run loop is active.
+ HttpStatsService(asio::io_context& IoContext, bool EnableWebSockets = true);
+
~HttpStatsService();
+ void Shutdown();
+
virtual const char* BaseUri() const override;
virtual void HandleRequest(HttpServerRequest& Request) override;
virtual void RegisterHandler(std::string_view Id, IHttpStatsProvider& Provider) override;
virtual void UnregisterHandler(std::string_view Id, IHttpStatsProvider& Provider) override;
+ // IWebSocketHandler
+ void OnWebSocketOpen(Ref<WebSocketConnection> Connection) override;
+ void OnWebSocketMessage(WebSocketConnection& Conn, const WebSocketMessage& Msg) override;
+ void OnWebSocketClose(WebSocketConnection& Conn, uint16_t Code, std::string_view Reason) override;
+
private:
LoggerRef m_Log;
HttpRequestRouter m_Router;
@@ -28,6 +55,22 @@ private:
RwLock m_Lock;
std::map<std::string, IHttpStatsProvider*> m_Providers;
+
+ // WebSocket push
+ RwLock m_WsConnectionsLock;
+ std::vector<Ref<WebSocketConnection>> m_WsConnections;
+ std::atomic<bool> m_PushEnabled{false};
+
+ void BroadcastStats();
+
+ // Thread-based push (when no io_context is provided)
+ std::thread m_PushThread;
+ Event m_PushEvent;
+ void PushThreadFunction();
+
+ // Timer-based push (when an io_context is provided)
+ std::unique_ptr<asio::steady_timer> m_PushTimer;
+ void EnqueuePushTimer();
};
} // namespace zen