TraceMaker
C++ API of the engine
Loading...
Searching...
No Matches
viewer_server.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2#pragma once
3// HTTP + WebSocket server for the live viewer (design doc 09, protocol in docs/13-viewer-protocol.md).
4//
5// Serves the built viewer (static files) at http://<host>:<port>/ and streams protocol messages at /ws.
6// publish() is thread-safe and never blocks on the network: messages are queued and fanned out by a single
7// background I/O thread. The server keeps the latest `board` snapshot plus the state changes since it, so a
8// client that connects mid-run sees the current scene. Slow clients lose transient messages (frontier,
9// path_try) and get coalesced stats; a client that falls hopelessly behind is disconnected (it reconnects and
10// receives a fresh snapshot).
11#include <cstddef>
12#include <cstdint>
13#include <memory>
14#include <string>
15
16#include "core/events.hpp"
17
18namespace tmk::server {
19
20// Who can reach the viewer (issue #4, docs/09-visualization.md "Security", D116): by default only this machine. A viewer bound to any other address
21// requires a token on the page and on /ws (a random one unless `token` is given; `allow_open` serves without, a deliberate choice that is said so),
22// and every WebSocket upgrade must come from the server's own origin, so a web page open in the user's browser cannot read the stream.
23// The bind address when none is given: $TM_VIEW_HOST if set, else 127.0.0.1 (this machine only).
24std::string default_host();
25
27 std::string host = default_host(); // "0.0.0.0" or a LAN address makes the viewer reachable from the network: it then needs a token
28 std::uint16_t port = 8766; // 0 = pick a free port (tests)
29 std::string token; // at least 16 characters of [A-Za-z0-9._~-]; empty = generated when the host is not a loopback address
30 bool allow_open = false; // a non-loopback host without any token (--view-insecure)
31 std::string embed; // pages that may frame the viewer, as CSP sources separated by spaces (--view-embed); empty = nobody (X-Frame-Options: DENY)
32 std::size_t max_clients = 16; // WebSocket viewers, counted from the upgrade on (a burst cannot overshoot); the next gets 503
33 std::size_t max_connections = 128; // concurrent HTTP connections (a page load is one); beyond it a connection is closed at once
34 std::size_t max_connections_per_address = 16; // of those, from one address: one host cannot use them all up before it has authenticated
35 std::string web_root; // directory with index.html; empty = <source dir>/viewer/dist
36 std::size_t max_queue_msgs = 2000; // per client: beyond this, transient messages are dropped
37 std::size_t max_queue_bytes = 8u << 20; // per client: same, by bytes
38 std::size_t disconnect_bytes = 256u << 20; // per client: state backlog that drops the connection
39};
40
41class ViewerServer final : public events::Sink {
42 public:
43 // Binds and starts the I/O thread. Throws std::runtime_error if the address cannot be bound.
44 explicit ViewerServer(ServerOptions opts = {});
45 ~ViewerServer() override;
46 ViewerServer(const ViewerServer&) = delete;
48
49 void publish(std::string json) override;
50 // True while at least one viewer is connected, so producers can skip building transient messages.
51 bool wants_transient() const override;
52
53 std::uint16_t port() const; // bound port (useful with port 0)
54 std::string url() const; // http://<host or a LAN address>:<port>/ , with ?token=... when the viewer needs one
55 const std::string& token() const; // "" = none required
56 bool exposed() const; // bound to an address other machines can reach
57 // What to tell the user when the viewer is reachable from the network ("" when it is not): who can see it and what protects it.
58 std::string notice() const;
59 std::size_t client_count() const;
60 const std::string& web_root() const;
61
62 struct Counters {
63 std::uint64_t published = 0, sent = 0, dropped = 0, clients_total = 0;
64 };
66
67 private:
68 struct Impl;
69 std::unique_ptr<Impl> impl_;
70};
71
72// Default directory of the built viewer: $TM_VIEW_DIR, else <prefix>/share/tracemaker/viewer for a binary in <prefix>/bin, else <repo>/viewer/dist.
73std::string default_web_root();
74
75} // namespace tmk::server
ViewerServer(ServerOptions opts={})
Binds and starts the I/O thread. Throws std::runtime_error if the address cannot be bound.
std::string notice() const
What to tell the user when the viewer is reachable from the network ("" when it is not): who can see ...
Counters counters() const
std::uint16_t port() const
bound port (useful with port 0)
ViewerServer & operator=(const ViewerServer &)=delete
ViewerServer(const ViewerServer &)=delete
bool exposed() const
bound to an address other machines can reach
bool wants_transient() const override
True while at least one viewer is connected, so producers can skip building transient messages.
std::string url() const
http://<host or a LAN address>:<port>/ , with ?token=... when the viewer needs one
const std::string & token() const
"" = none required
void publish(std::string json) override
One JSON object per message (see the protocol document for the message types).
const std::string & web_root() const
std::size_t client_count() const
Event sink for live visualisation and replay (design doc 02 §7, protocol in docs/13-viewer-protocol....
std::string default_web_root()
Default directory of the built viewer: $TM_VIEW_DIR, else <prefix>/share/tracemaker/viewer for a bina...
std::string default_host()
Who can reach the viewer (issue #4, docs/09-visualization.md "Security", D116): by default only this ...
std::size_t max_clients
WebSocket viewers, counted from the upgrade on (a burst cannot overshoot); the next gets 503.
std::size_t max_queue_bytes
per client: same, by bytes
std::string web_root
directory with index.html; empty = <source dir>/viewer/dist
std::size_t max_queue_msgs
per client: beyond this, transient messages are dropped
std::size_t max_connections_per_address
of those, from one address: one host cannot use them all up before it has authenticated
std::size_t disconnect_bytes
per client: state backlog that drops the connection
std::string token
at least 16 characters of [A-Za-z0-9._~-]; empty = generated when the host is not a loopback address
std::string host
"0.0.0.0" or a LAN address makes the viewer reachable from the network: it then needs a token
bool allow_open
a non-loopback host without any token (–view-insecure)
std::string embed
pages that may frame the viewer, as CSP sources separated by spaces (–view-embed); empty = nobody (X-...
std::size_t max_connections
concurrent HTTP connections (a page load is one); beyond it a connection is closed at once
std::uint16_t port
0 = pick a free port (tests)