|
Aria 2.0.0
C++23 MVVM framework (C++20 minimum) — reactive, coroutine-first, ABI-layered
|
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.
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.
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.
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.
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": "…" }.
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.
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.
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.
| 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.