Aria 2.0.0
C++23 MVVM framework (C++20 minimum) — reactive, coroutine-first, ABI-layered
Loading...
Searching...
No Matches
HTTP / REST / SSE Adapter

The HTTP adapter exposes registered logical views to browsers using REST and Server-Sent Events. It implements the same IViewAdapter contract as native UI adapters. Build with -DARIA_BUILD_HTTP=ON; add -DARIA_HTTP_ENABLE_TLS=OFF for a plain HTTP development build. Link the application to aria::http and aria::runtime when using SimpleDispatcher.

A compiled starting point

This complete example is compiled and run as http_guide_example in CTest. It starts and stops immediately; in an application, keep the server alive and pump the dispatcher from the thread that owns the reactive graph, or supply the host's existing IDispatcher. Binding teardown also belongs on that thread.

#include <memory>
#include <string>
int main() {
config.port = 0;
config.worker_threads = 4;
auto http = std::make_shared<aria::adapters::http::HttpAdapter>(config);
auto dispatcher = std::make_shared<aria::runtime::SimpleDispatcher>();
aria::binding::BindingEngine engine(http, dispatcher,
auto& search = http->register_view("search_query", "text");
engine.bind_text(query, search);
if (!http->start()) return 1;
// A real host repeatedly pumps this dispatcher on the graph owner thread.
dispatcher->pump();
http->stop();
return http->actual_port() == 0 ? 0 : 1;
}
BindingEngine: connects ViewModel properties to platform views via an adapter.
Definition binding_engine.hpp:103
@ SmartMarshal
inline iff dispatcher.is_main_thread()
Definition binding_engine.hpp:108
Definition property.hpp:103
HTTP/REST/SSE implementation of IViewAdapter.
Configuration for HttpAdapter.
Definition http_config.hpp:15
std::uint16_t port
TCP port.
Definition http_config.hpp:23
int worker_threads
Worker thread count for the HTTP server.
Definition http_config.hpp:37

start() waits for listening readiness and returns false on failure or if already running. actual_port() reports the selected port and resets to zero on stop. stop() closes streams and interrupts the heartbeat wait. Call start/stop from the host lifecycle thread, outside HTTP callbacks.

Supported view kinds: text, bool, int, int64, uint64, float, double, click. Use bind_command(command, click_view) for commands. Registering an existing ID replaces its view, drops old subscriptions and custom commands, and clears its shadow state. Existing references to that view become invalid; bind the replacement returned by register_view.

Endpoints and protocol 2

All endpoints use config.api_prefix, default /aria.

Method and path Purpose
GET /aria/health Readiness and protocol version
GET /aria/views { "views": [{"id":"…", "kind":"…"}] }
GET /aria/state?view=X {view, kind, value, visible, enabled}
POST /aria/state {view, field, value}
POST /aria/click {view} for a click view
POST /aria/command {view, command, args} for a custom handler
GET /aria/stream SSE stream

The server serves files at / only when static_root points to a directory. There is no built-in dashboard or automatic SDK mount.

SSE frames contain JSON on data: lines. Initial delivery is hello, then registered views' current values (except click views), visibility and enabled state. Unset values are null; visibility and enabled default to true. Initial snapshots precede subsequent state changes for that connection.

{"type":"hello","platform":"http","protocol":2}
{"type":"state","view":"search_query","field":"text","value":"hello"}
{"type":"visibility","view":"search_query","value":true}
{"type":"enabled","view":"search_query","value":true}

int64 and uint64 values outside JavaScript's safe integer range [-9007199254740991, 9007199254740991] travel as exact decimal strings with the same field tag. Safe values remain JSON numbers. The SDK returns unsafe values as BigInt. For display use String(value); for JSON serialization explicitly convert BigInt to a decimal string. A protocol 1 client that assumes every numeric field is a Number must be upgraded together with the server.

The server accepts range-checked integer JSON tokens and, for 64-bit fields, decimal strings. The SDK accepts safe Number, bigint or decimal string for setInt64 / setUInt64; it rejects already-rounded unsafe Numbers. Fractional integers, bool-as-number, negative unsigned values and out-of-range numbers receive 400. Bad JSON/schema or a field that mismatches its view also receive 400; unknown views/commands receive 404. Errors carry { "error": "…" }.

Browser SDK

Copy modules/adapters/http/web-sdk/aria_client.js into your served static root and import it as an ES module. No npm dependencies are needed.

<script type="module">
import { AriaClient } from './aria_client.js';
const client = new AriaClient(location.origin);
client.onOpen(() => console.log('connected'));
client.onError(error => console.error(error));
client.subscribe('search_query', value => {
document.getElementById('result').textContent = value ?? '';
});
await client.connect();
await client.setText('search_query', 'hello world');
// On component/application teardown: client.close();
</script>

Connection errors clear isConnected(); EventSource can reconnect automatically, and each new hello triggers onOpen. Closing rejects a pending connection. Fetch helpers reject non-success HTTP statuses. State subscriptions can also use an explicit channel: client.subscribe(viewId, callback, "visibility") or client.getState(viewId, "enabled"). View IDs are never split on dots. Each returned unsubscribe function owns one registration, is idempotent, and cancels callbacks that have not started.

Threading and extensibility

State and click subscriptions run on server worker threads. Their notification batches are serialized in the order that updates enter the registry; reentrant updates are queued after the current batch. Releasing a subscription cancels a callback that has not begun, and a throwing callback is reported without stopping siblings. Custom command handlers can execute concurrently on different workers. Use BindingEngine with a dispatcher and SmartMarshal or AlwaysPost to route bound Property and Command work onto the graph owner thread. A successful POST acknowledges validation and dispatch; queued model work may still be pending. Direct adapter subscriptions and custom handlers must explicitly marshal any graph access themselves.

The pending notification limit returns 503 before accepting another state or click update. A queued notification can be cancelled by removing its view or destroying the adapter before delivery. Call start() and stop() from the host lifecycle thread; calling either from the adapter's worker callbacks raises std::logic_error. Destruction from a callback closes the adapter and lets the retained worker state finish teardown.

Registry maps and shadow state are mutex protected; getters return copies. Setters enqueue SSE data without waiting for socket writes. The returned view references are not lifetime pins: serialize registration/replacement/removal with binding and native view use on the graph owner thread. Avoid removing a view while another thread dereferences it.

register_command(view_id, name, handler) receives a JSON argument string and returns a JSON response string. The view must exist when invoked. native_server() allows custom cpp-httplib routes before start, but is an unstable escape hatch. The installed aria::http target supplies <httplib.h> and its matching compile/link requirements. Replacing its worker queue or blocking custom handlers changes the adapter's capacity assumptions.

Configuration

Field Default Meaning
host 127.0.0.1 Bind address
port 9090 0 selects an available port
api_prefix /aria REST/SSE path prefix
static_root empty Optional static-file directory
worker_threads 0 Fixed pool; 0 detects CPU count, minimum 2; explicit counts must be ≥2
max_sse_clients 64 Also capped at workers minus one; 0 removes only this extra cap
max_pending_sse_bytes 4194304 Positive per-client queued-byte limit; an overflowing stream closes and can reconnect for a fresh snapshot
max_pending_notifications 1024 Positive limit on queued state/click notification batches; excess updates receive 503
heartbeat_sec 25 Positive interval in seconds
enable_cors false Adds permissive CORS headers when enabled
tls_cert_file, tls_key_file empty PEM pair for a TLS-enabled build
tls_ca_file empty Optional client-certificate verification CA
tls_min_version 1.2 1.2 or 1.3

Excess SSE connections receive 503 so a worker remains available for REST. An initial snapshot exceeding the per-client queue limit also receives 503. This is a connection limit, not event rate limiting. Long-running custom REST handlers can still consume the remaining workers. The default address is local; external deployments must provide their own authentication/network boundary.

See binding, the protocol header and historical RFC.