pub struct DashboardServer { /* private fields */ }Expand description
A minimal HTTP server that serves the dashboard HTML and an SSE endpoint.
The server uses a shared Arc<Mutex<DashboardMetrics>> so that an
external thread can update the metrics while the server pushes them to
connected SSE clients.
Implementations§
Source§impl DashboardServer
impl DashboardServer
Sourcepub fn new(
config: DashboardConfig,
metrics: Arc<Mutex<DashboardMetrics>>,
) -> Self
pub fn new( config: DashboardConfig, metrics: Arc<Mutex<DashboardMetrics>>, ) -> Self
Create a new server with the given config and a shared metrics handle.
Sourcepub fn metrics_handle(&self) -> Arc<Mutex<DashboardMetrics>> ⓘ
pub fn metrics_handle(&self) -> Arc<Mutex<DashboardMetrics>> ⓘ
Return a clone of the shared metrics handle so callers can update it.
Sourcepub fn run(&self) -> Result<()>
pub fn run(&self) -> Result<()>
Start listening. This blocks the calling thread.
Each accepted connection is served on its own thread, so a
long-lived /events SSE stream (or a half-open client) can never
block the accept loop. See Self::run_on for the routing table
and the history of issue #36.
Sourcepub fn run_on(&self, listener: TcpListener) -> Result<()>
pub fn run_on(&self, listener: TcpListener) -> Result<()>
Serve on an already-bound listener. This blocks the calling thread.
Split out from Self::run so tests can bind port 0 and learn the
real port via listener.local_addr() before starting the server.
For each incoming connection the server reads the HTTP request line, then either:
GET /→ responds with the full HTML pageGET /events→ responds with an SSE stream (pushes metrics JSON every 5 seconds until the client disconnects)- anything else → 404
Concurrency model: one thread per connection. The previous design
served every connection inline on the accept thread, which meant a
single connected SSE client parked the whole server in its push
loop, and an interrupted SSE client parked it for up to two 5-second
ticks (the first write after a peer FIN succeeds into the send
buffer; only the next one observes the RST — and TcpStream::flush
is a no-op, so it detects nothing). Meanwhile every other connection
sat unaccepted in the kernel backlog showing CLOSE_WAIT, and the
dashboard page’s EventSource auto-reconnect turned the pile-up into
a permanent hang (issue #36).