|
morph 0.1.0
Asynchronous bridge between GUI thread and business models
|
IBackend implementation that communicates with a RemoteServer over a raw-socket RFC 6455 WebSocket connection — no Qt, no GUI event loop required. More...
#include <socket_backend.hpp>
Public Types | |
| using | Config = SocketBackendConfig |
| Alias for the reconnect configuration struct. | |
Public Member Functions | |
| SocketBackend (::morph::exec::IoLoop &loop, std::string_view serverUrl, Config cfg={}) | |
Parses serverUrl and starts connecting on loop. | |
| SocketBackend (std::string_view serverUrl, Config cfg={}) | |
Parses serverUrl and starts connecting on a loop of its own, for a caller with no IoLoop to share. | |
| SocketBackend (const SocketBackend &)=delete | |
| SocketBackend & | operator= (const SocketBackend &)=delete |
| SocketBackend (SocketBackend &&)=delete | |
| SocketBackend & | operator= (SocketBackend &&)=delete |
| ~SocketBackend () override | |
| Closes the connection and rejects every pending call with DisconnectedError. | |
| bool | waitForConnected (std::chrono::milliseconds timeout=std::chrono::milliseconds{5000}) |
Blocks the calling thread until connected or timeout elapses. | |
| ::morph::exec::detail::ModelId | registerModel (const std::string &typeId, std::function< std::unique_ptr<::morph::model::detail::IModelHolder >()> factory) override |
| Registers a private instance and waits for the reply. | |
| ::morph::exec::detail::ModelId | registerModelWithContext (const std::string &typeId, std::function< std::unique_ptr<::morph::model::detail::IModelHolder >()> factory, std::string_view contextKey) override |
Registers a private instance carrying contextKey and waits for the reply. | |
| void | assignPrimary (::morph::exec::detail::ModelId mid, const std::string &typeId, std::string_view primary) override |
Files a live server-side instance under primary and waits for the reply. | |
| ::morph::async::Completion<::morph::exec::detail::ModelId > | bindModel (::morph::backend::detail::BindRequest request, ::morph::exec::IExecutor &cbExec) override |
| Acquires a model instance without blocking the calling thread. | |
| ::morph::async::Completion<::morph::exec::detail::ModelId > | promoteModel (::morph::backend::detail::PromoteRequest request, ::morph::exec::IExecutor &cbExec) override |
| Files an already-live server-side instance under a key, without blocking the calling thread. | |
| std::vector< std::string > | listInstances (const std::string &typeId) override |
Asks the server for the live shared primary keys of typeId and waits for the reply, never on the loop's own thread. | |
| ::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), on the callId-multiplexed control path, and settles with its reply. | |
| ::morph::async::Completion<::morph::wire::ProtocolNegotiationResult > | negotiateProtocolVersion (::morph::exec::IExecutor &replyExec) |
| Sends a "hello" envelope and classifies the server's 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 resolved on reply. | |
| void | notifyBackendChanged () override |
| No-op — this backend holds no local model objects. | |
| void | cancelPending (const std::exception_ptr &exc) override |
Resolves every pending call's Completion with exc. | |
| void | setReconnectHandler (std::function< void()> handler, ::morph::exec::IExecutor *exec) override |
Installs the handler posted to exec after each subsequent successful (re)connect. | |
| void | setConnectHandler (const std::function< void()> &handler) override |
| Installs the handler run after every successful connect, the first included. | |
| 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, instances). See IBackend::setSession. | |
| Public Member Functions inherited from morph::backend::IEnvelopeTransport | |
| IEnvelopeTransport (IEnvelopeTransport const &)=delete | |
| IEnvelopeTransport & | operator= (IEnvelopeTransport const &)=delete |
| IEnvelopeTransport (IEnvelopeTransport &&)=delete | |
| IEnvelopeTransport & | operator= (IEnvelopeTransport &&)=delete |
IBackend implementation that communicates with a RemoteServer over a raw-socket RFC 6455 WebSocket connection — no Qt, no GUI event loop required.
Mirrors morph::qt::QtWebSocketBackend's observable behavior (deregisterModel fire-and-forget, execute and bindModel asynchronous and callId-multiplexed, DisconnectedError/reconnect semantics). Its socket, its pending calls, its session, its handlers and its reconnect state machine live on an exec::IoLoop, which it shares with every other socket, timer and probe the application builds on that loop.
Cross-thread surface: execute, bindModel, promoteModel, request, deregisterModel, cancelPending, setSession, setReconnectHandler, setConnectHandler and setEventHandler post to the loop and return, so calls from one thread take effect in the order they were made; waitForConnected posts a waiter and blocks the caller, never the loop; the synchronous control verbs (registerModel, registerModelWithContext, assignPrimary, listInstances) issue the same posted request and wait for its reply, never on the loop's own thread. Every field is touched only on the loop, except the connected flag.
Definition at line 96 of file socket_backend.hpp.
Alias for the reconnect configuration struct.
Definition at line 99 of file socket_backend.hpp.
|
inline |
Parses serverUrl and starts connecting on loop.
| loop | The application's I/O loop. Borrowed: it must outlive this backend. |
| serverUrl | ws://host:port[/path] URL of the remote RemoteServer. |
| cfg | Reconnect tuning. Default: enabled, 500ms initial / 30s cap, 2x backoff. |
| std::runtime_error | immediately if serverUrl is not a well-formed ws:// URL (see parseWsUrl). |
Definition at line 108 of file socket_backend.hpp.
|
inlineexplicit |
Parses serverUrl and starts connecting on a loop of its own, for a caller with no IoLoop to share.
| serverUrl | ws://host:port[/path] URL of the remote RemoteServer. |
| cfg | Reconnect tuning. Default: enabled, 500ms initial / 30s cap, 2x backoff. |
| std::runtime_error | immediately, before any loop is started, if serverUrl is not a well-formed ws:// URL (see parseWsUrl). |
Definition at line 121 of file socket_backend.hpp.
|
inlineoverride |
Closes the connection and rejects every pending call with DisconnectedError.
The close runs on the loop: inline when this runs on the loop's own thread (from a callback), posted and waited for otherwise. It never waits for a connect in progress: a dial that completes afterwards finds the backend closed and drops its socket.
Definition at line 142 of file socket_backend.hpp.
|
inline |
Blocks the calling thread until connected or timeout elapses.
Posts a waiter to the loop, which releases it when a connection completes. On the loop's own thread it cannot wait for the loop, so it answers at once.
| timeout | Maximum time to wait. |
Definition at line 156 of file socket_backend.hpp.
|
inlineoverride |
Registers a private instance and waits for the reply.
The same request bindModel posts, waited for on the calling thread — which must not be the I/O loop's, whose reply this would wait on. The factory argument is ignored — model construction is delegated to the server.
| typeId | String type-id of the model to register. |
| factory | Ignored — the server constructs via its own registry. |
| std::runtime_error | if the server replies with an error, the socket is not connected, or this is the loop's own thread. |
Definition at line 178 of file socket_backend.hpp.
|
inlineoverride |
Registers a private instance carrying contextKey and waits for the reply.
The server constructs the holder itself, so contextKey is the only channel by which the instance's identity reaches it: RemoteServer::attachLogIfConfigured consults its LogProvider only for a non-empty one. Same waiting rule as registerModel.
| typeId | String type-id of the model to register. |
| factory | Ignored — the server constructs via its own registry. |
| contextKey | Stable identity of the new instance; empty if none. |
| std::runtime_error | if the server replies with an error, the socket is not connected, or this is the loop's own thread. |
Definition at line 196 of file socket_backend.hpp.
|
inlineoverride |
Files a live server-side instance under primary and waits for the reply.
| mid | Live instance to promote. |
| typeId | Model type id. |
| primary | Canonical string encoding of the key to file it under. |
Definition at line 213 of file socket_backend.hpp.
|
inlineoverride |
Acquires a model instance without blocking the calling thread.
Sends the control envelope request's shape names (see backend::detail::BindRequest's table) carrying a non-zero callId drawn from the same counter execute uses, and settles the returned Completion on the I/O loop when the matching reply arrives. No thread is parked anywhere.
An empty primary with a live current gives that instance up first, and an empty primary binds a private instance.
A private bind made while the socket is not connected is queued, not failed, and sent in FIFO order the next time it connects, the first connect included: a single-threaded client binds before the socket is up and cannot block waiting for it. cancelPending and the backend's destruction reject what is still queued. A keyed bind is not queued: it may re-point a live instance, and replayed on a later connection it would attach from a current that connection never issued.
| request | Owning bind request; moved from. |
| cbExec | Executor the continuation is delivered on. Borrowed: it must outlive the returned Completion. |
Definition at line 261 of file socket_backend.hpp.
|
inlineoverride |
Files an already-live server-side instance under a key, without blocking the calling thread.
The promote counterpart of bindModel, on the same callId-multiplexed path. Resolves with request.mid echoed back exactly as IBackend::promoteModel documents — including for the two guards assignPrimary applies locally (empty primary, zero mid), which resolve without sending anything.
| request | Owning promote request; moved from. |
| cbExec | Executor the continuation is delivered on. Borrowed: it must outlive the returned Completion. |
Definition at line 295 of file socket_backend.hpp.
|
inlineoverride |
Asks the server for the live shared primary keys of typeId and waits for the reply, never on the loop's own thread.
| typeId | String type-id to enumerate. |
| std::runtime_error | if the server replies with an error, the socket is down, or this is the loop's own thread. |
Definition at line 313 of file socket_backend.hpp.
|
inlineoverridevirtual |
Sends one request envelope of a kind the other verbs do not build (IEnvelopeTransport::request), on the callId-multiplexed control path, and settles with its reply.
Posted to the loop, which assigns the callId and sends the envelope with the session it carries. Made while the socket is not connected, it is held and sent at the next connect, the first included, as a private bind is, so a client can ask before the socket is up. cancelPending, a drop and the backend's destruction reject what is pending or held.
| envelope | The request; its callId is replaced. |
| cbExec | Executor the answer is delivered on. Borrowed: it must outlive the returned Completion. |
Implements morph::backend::IEnvelopeTransport.
Definition at line 342 of file socket_backend.hpp.
|
inlinenodiscard |
Sends a "hello" envelope and classifies the server's reply.
Opt-in, as on the other backends: a backend that never calls it sends no "hello". Calling it also learns whether the server honours "cancel" (wire::kCapabilityCancel), which is the only way this backend ever sends one; and it re-sends "hello" on every reconnect, since a new connection may reach a different server.
| replyExec | Executor the answer is delivered on. Borrowed: it must outlive the returned Completion. |
Definition at line 372 of file socket_backend.hpp.
|
inlineoverride |
Sends a deregister message fire-and-forget (does not wait for a reply).
Posted to the loop, which gives it a real, non-zero callId drawn from the same counter execute() uses and stamps the session.
The reply router drops any callId that is not pending, and a deregister files none, so its reply is dropped.
| mid | Id of the model to remove on the server. |
Definition at line 394 of file socket_backend.hpp.
|
inlineoverride |
Sends an execute message and returns a Completion resolved on reply.
The action is serialised here, on the calling thread; the loop then assigns the call id, files the pending record and writes the frame.
| mid | Target model id on the server. |
| call | Bundled action; serializeAction and deserializeResult are used. |
| cbExec | Executor for delivering the completion callbacks. |
Definition at line 412 of file socket_backend.hpp.
|
inlineoverride |
No-op — this backend holds no local model objects.
Definition at line 447 of file socket_backend.hpp.
|
inlineoverride |
Resolves every pending call's Completion with exc.
Posted to the loop, so it covers every call issued before it — the loop runs posts in order. Covers both in-flight tables: the execute calls and the bindModel/promoteModel control calls. A bind left out of this sweep would hang forever on a disconnect, since its reply can now only arrive on a connection that is gone. When the server advertised "cancel", each abandoned execute is also cancelled on the server, from the loop, behind whatever the loop had queued before.
| exc | Exception delivered to every pending completion's error sink. |
Definition at line 459 of file socket_backend.hpp.
|
inlineoverride |
Installs the handler posted to exec after each subsequent successful (re)connect.
Stored on the loop. After a reconnect the loop posts the handler to exec and goes on: whatever the handler does — a Bridge re-registering its bindings through bindModel — never runs on the loop whose replies it needs.
| handler | Callable posted to exec. Pass nullptr to clear. |
| exec | Executor the handler runs on. Borrowed: it must outlive the installation. |
Definition at line 477 of file socket_backend.hpp.
|
inlineoverride |
Installs the handler run after every successful connect, the first included.
Stored on the loop, and run there: after the requests held while the socket was down have been sent, and before the reconnect handler is posted. A handler that throws is logged; the connection carries on.
| handler | Callable run on the backend's I/O loop. Pass nullptr to clear. |
Definition at line 493 of file socket_backend.hpp.
|
inlineoverride |
Installs the handler each server-initiated event is posted to. See IBackend::setEventHandler.
Stored on the loop. An event envelope is recognised by its kind before any reply matching, so whatever its callId, it never settles a call. Events are posted to exec in the order they arrived.
| handler | Callable posted to exec once per event. Pass nullptr to clear. |
| exec | Executor the handler is posted to. Borrowed: it must outlive the installation. May be null only when handler is. |
Definition at line 511 of file socket_backend.hpp.
|
inlineoverride |
Installs the session stamped onto every control envelope this backend subsequently builds (register, registerShared, attach, assign, deregister, instances). See IBackend::setSession.
Stored on the loop, which stamps every control envelope as it files it: a verb called after this, from the same thread, carries it.
| session | Session to stamp; typically pushed by Bridge::setDefaultSession(). |
Definition at line 530 of file socket_backend.hpp.