|
| | QtWebSocketBackend (QUrl serverUrl, ::morph::model::detail::ActionDispatcher &dispatcher=::morph::model::detail::defaultDispatcher(), ::morph::model::detail::ModelRegistryFactory ®istry=::morph::model::detail::defaultRegistry(), std::optional< QSslConfiguration > tls=std::nullopt, Config cfg=Config{}) |
| | Constructs the backend and opens a WebSocket connection to serverUrl.
|
| | QtWebSocketBackend (QUrl serverUrl, std::optional< QSslConfiguration > tls, Config cfg=Config{}) |
| | Constructs the backend without naming the dispatcher/registry pair.
|
| | QtWebSocketBackend (QUrl serverUrl, Config cfg) |
| | Constructs the backend without naming the dispatcher/registry pair or TLS.
|
|
| ~QtWebSocketBackend () override |
| | Closes the socket and cleans up pending operations.
|
|
| QtWebSocketBackend (const QtWebSocketBackend &)=delete |
|
QtWebSocketBackend & | operator= (const QtWebSocketBackend &)=delete |
|
| QtWebSocketBackend (QtWebSocketBackend &&)=delete |
|
QtWebSocketBackend & | operator= (QtWebSocketBackend &&)=delete |
| bool | waitForConnected (int timeoutMs=5000) |
| | Pumps the Qt event loop until the socket is connected or timeoutMs elapses.
|
| ::morph::async::Completion<::morph::wire::ProtocolNegotiationResult > | negotiateProtocolVersion (::morph::exec::IExecutor &replyExec) |
| | Sends a "hello" envelope to the server and classifies its reply.
|
| ::morph::exec::detail::ModelId | registerModel (const std::string &typeId, std::function< std::unique_ptr<::morph::model::detail::IModelHolder >()> factory) override |
| | Refuses: this backend registers only through bindModel.
|
| ::morph::async::Completion<::morph::exec::detail::ModelId > | bindModel (::morph::backend::detail::BindRequest request, ::morph::exec::IExecutor &cbExec) override |
| | Acquires a model instance over the wire, settled by the server's reply.
|
| void | assignPrimary (::morph::exec::detail::ModelId mid, const std::string &typeId, std::string_view primary) override |
| | Refuses: this backend promotes only through promoteModel.
|
| ::morph::async::Completion<::morph::exec::detail::ModelId > | promoteModel (::morph::backend::detail::PromoteRequest request, ::morph::exec::IExecutor &cbExec) override |
| | Files a live server-side instance under a key, settled by the server's reply.
|
| std::vector< std::string > | listInstances (const std::string &typeId) override |
| | Refuses: this backend lists instances only through instances.
|
| ::morph::async::Completion< std::vector< std::string > > | instances (const std::string &typeId, ::morph::exec::IExecutor &cbExec) override |
| | Asks the server for the live shared primary keys of typeId.
|
| ::morph::async::Completion<::morph::wire::Envelope > | request (::morph::wire::Envelope envelope, ::morph::exec::IExecutor &cbExec) override |
| | Sends one request envelope of a kind the other verbs do not build (IEnvelopeTransport::request) and settles with its reply.
|
| void | deregisterModel (::morph::exec::detail::ModelId mid) override |
| | Sends a deregister message fire-and-forget (does not wait for a reply).
|
| ::morph::async::Completion< std::shared_ptr< void > > | execute (::morph::exec::detail::ModelId mid, ::morph::backend::detail::ActionCall call, ::morph::exec::IExecutor *cbExec) override |
| | Sends an execute message and returns a Completion that resolves on reply.
|
| void | notifyBackendChanged () override |
| | No-op — this backend holds no local model objects.
|
| void | cancelPending (const std::exception_ptr &exc) override |
| | Rejects every in-flight request's Completion with exc — executes, binds (queued ones included), promotes, instance listings and the hello alike.
|
| void | setReconnectHandler (std::function< void()> handler, ::morph::exec::IExecutor *exec) override |
| | Installs the handler Bridge uses to re-register handlers after a reconnect.
|
| void | setConnectHandler (const std::function< void()> &handler) override |
| | Installs a handler invoked on every successful connect, including the first.
|
| void | setDisconnectHandler (const std::function< void()> &handler) override |
| | Installs a handler invoked whenever the socket drops.
|
| void | setEventHandler (std::function< void(::morph::wire::EventBody)> handler, ::morph::exec::IExecutor *exec) override |
| | Installs the handler each server-initiated event is posted to. See IBackend::setEventHandler.
|
| void | setSession (::morph::session::Context session) override |
| | Installs the session stamped onto every control envelope this backend subsequently builds (register, registerShared, attach, assign, deregister). See IBackend::setSession.
|
|
| IEnvelopeTransport (IEnvelopeTransport const &)=delete |
|
IEnvelopeTransport & | operator= (IEnvelopeTransport const &)=delete |
|
| IEnvelopeTransport (IEnvelopeTransport &&)=delete |
|
IEnvelopeTransport & | operator= (IEnvelopeTransport &&)=delete |
IBackend implementation that communicates with a RemoteServer over WebSocket.
Every request is asynchronous and nothing blocks the Qt thread: a request carries a fresh non-zero callId, its pending entry is filed under it, and the matching reply settles that entry from the textMessageReceived slot. The caller's Completion is delivered on the executor the caller names. The request verbs are execute(), bindModel()/promoteModel() (the structural registration surface, docs/spec/core/backend.md), instances() and negotiateProtocolVersion(). A bind issued before the socket is up leaves its handler unbound; a call made through it is held by the Bridge and dispatched when the bind settles.
The synchronous IBackend verbs that would have to wait for a reply — registerModel(), assignPrimary(), listInstances() — throw std::logic_error naming their completion form: waiting would mean spinning a nested event loop on the Qt thread, which a WASM main thread cannot do at all.
Everything this backend keeps — the socket, the pending tables — belongs to the thread the socket lives on, as Qt's own objects do: every verb is called there (the Bridge's owner is that thread) and every reply slot runs there, so none of it takes a lock. A debug build asserts it at each site. deregisterModel() is fire-and-forget: it sends the message and files only its callId, so the reply can be recognised and dropped.
- TLS
- Pass a QSslConfiguration to enable wss://. Build it with tlsVerifyingConfig() (CA-verified — the recommended production default) or tlsPinnedConfig() (pinned-certificate — the correct choice for a self-signed deployment), both in qt_tls.hpp. tlsInsecureNoVerify() disables peer verification entirely and is for local development and tests only — see security.md's "Transport security" section.
- SSL-less Qt builds (QT_NO_SSL, including the standard Qt-for-WebAssembly build)
- The constructor's tls parameter (and the _tls member) does not exist at all when Qt itself was configured without SSL — QSslConfiguration isn't a type Qt provides in that configuration, so there is no value to accept or ignore. wss:// still works on such a build regardless: in a WASM/browser deployment the browser terminates TLS before Qt's QWebSocket ever sees the connection, so the only thing genuinely unavailable is the ability to configure TLS from C++ (client certificates, pinning, etc.) — plain wss:// and ws:// both still connect normally.
- Threading
- Must be used from the Qt event loop thread. Every verb and the internal message handler are called on that thread.
Definition at line 93 of file qt_websocket_backend.hpp.
Acquires a model instance over the wire, settled by the server's reply.
The structural registration surface (IBackend::bindModel). The request's shape selects the envelope:
| primary | current | Envelope sent |
| empty | 0 | register (private) |
| empty | non-zero | deregister of current, then register |
| non-empty | 0 | register with shared (register-or-attach) |
| non-empty | non-zero | attach, naming current |
Each carries a fresh callId from the counter execute() uses and the installed session; request.contextKey rides on every shape, since the server constructs the holder itself and the key is the only channel by which the instance's identity (and so its action log) reaches it.
A private registration issued before the socket has finished connecting is queued rather than failed: exactly the ordering a single-threaded WASM client must use, since it can never wait for the connection to settle. The queued request is sent — with a call-id assigned at that point — the moment connected fires next, first connect included, in FIFO order. If the backend is torn down first, cancelPending rejects each queued entry exactly once. A keyed bind on a disconnected socket rejects immediately instead: it may re-point a live instance, and replaying that against a later connection would attach from a current that connection never issued.
- Parameters
-
| request | Owning bind request; moved from. request.factory is unused — the server instantiates the model from request.typeId. |
| cbExec | Executor the continuation is delivered on. Borrowed: it must outlive the returned Completion. |
- Returns
- A Completion resolved with the bound ModelId, or rejected with the failure.