123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805 |
- #ifndef REALM_SYNC_CLIENT_HPP
- #define REALM_SYNC_CLIENT_HPP
- #include <cstddef>
- #include <cstdint>
- #include <memory>
- #include <utility>
- #include <functional>
- #include <exception>
- #include <string>
- #include <realm/util/buffer.hpp>
- #include <realm/util/functional.hpp>
- #include <realm/sync/client_base.hpp>
- #include <realm/sync/socket_provider.hpp>
- #include <realm/sync/subscriptions.hpp>
- namespace realm::sync {
- class Client {
- public:
- using port_type = sync::port_type;
- using Error = ClientError;
- static constexpr milliseconds_type default_connect_timeout = sync::default_connect_timeout;
- static constexpr milliseconds_type default_connection_linger_time = sync::default_connection_linger_time;
- static constexpr milliseconds_type default_ping_keepalive_period = sync::default_ping_keepalive_period;
- static constexpr milliseconds_type default_pong_keepalive_timeout = sync::default_pong_keepalive_timeout;
- static constexpr milliseconds_type default_fast_reconnect_limit = sync::default_fast_reconnect_limit;
- using Config = ClientConfig;
- /// \throw util::EventLoop::Implementation::NotAvailable if no event loop
- /// implementation was specified, and
- /// util::EventLoop::Implementation::get_default() throws it.
- Client(Config = {});
- Client(Client&&) noexcept;
- ~Client() noexcept;
- /// Run the internal event-loop of the client. At most one thread may
- /// execute run() at any given time. The call will not return until somebody
- /// calls stop().
- void run() noexcept;
- /// See run().
- ///
- /// Thread-safe.
- void shutdown() noexcept;
- /// Forces all connections to close and waits for any pending work on the event
- /// loop to complete. All sessions must be destroyed before calling shutdown_and_wait.
- void shutdown_and_wait();
- /// \brief Cancel current or next reconnect delay for all servers.
- ///
- /// This corresponds to calling Session::cancel_reconnect_delay() on all
- /// bound sessions, but will also cancel reconnect delays applying to
- /// servers for which there are currently no bound sessions.
- ///
- /// Thread-safe.
- void cancel_reconnect_delay();
- /// \brief Wait for session termination to complete.
- ///
- /// Wait for termination of all sessions whose termination was initiated
- /// prior this call (the completion condition), or until the client's event
- /// loop thread exits from Client::run(), whichever happens
- /// first. Termination of a session can be initiated implicitly (e.g., via
- /// destruction of the session object), or explicitly by Session::detach().
- ///
- /// Note: After session termination (when this function returns true) no
- /// session specific callback function can be called or continue to execute,
- /// and the client is guaranteed to no longer have a Realm file open on
- /// behalf of the terminated session.
- ///
- /// CAUTION: If run() returns while a wait operation is in progress, this
- /// waiting function will return immediately, even if the completion
- /// condition is not yet satisfied. The completion condition is guaranteed
- /// to be satisfied only when these functions return true. If it returns
- /// false, session specific callback functions may still be executing or get
- /// called, and the associated Realm files may still not have been closed.
- ///
- /// If a new wait operation is initiated while another wait operation is in
- /// progress by another thread, the waiting period of fist operation may, or
- /// may not get extended. The application must not assume either.
- ///
- /// Note: Session termination does not imply that the client has received an
- /// UNBOUND message from the server (see the protocol specification). This
- /// may happen later.
- ///
- /// \return True only if the completion condition was satisfied. False if
- /// the client's event loop thread exited from Client::run() in which case
- /// the completion condition may, or may not have been satisfied.
- ///
- /// Note: These functions are fully thread-safe. That is, they may be called
- /// by any thread, and by multiple threads concurrently.
- bool wait_for_session_terminations_or_client_stopped();
- /// Returns false if the specified URL is invalid.
- bool decompose_server_url(const std::string& url, ProtocolEnvelope& protocol, std::string& address,
- port_type& port, std::string& path) const;
- private:
- std::unique_ptr<ClientImpl> m_impl;
- friend class Session;
- };
- class BadServerUrl; // Exception
- /// \brief Client-side representation of a Realm file synchronization session.
- ///
- /// A synchronization session deals with precisely one local Realm file. To
- /// synchronize multiple local Realm files, you need multiple sessions.
- ///
- /// A session object is always associated with a particular client object (\ref
- /// Client). The application must ensure that the destruction of the associated
- /// client object never happens before the destruction of the session
- /// object. The consequences of a violation are unspecified.
- ///
- /// A session object is always associated with a particular local Realm file,
- /// however, a session object does not represent a session until it is bound to
- /// a server side Realm, i.e., until bind() is called. From the point of view of
- /// the thread that calls bind(), the session starts precisely when the
- /// execution of bind() starts, i.e., before bind() returns.
- ///
- /// At most one session is allowed to exist for a particular local Realm file
- /// (file system inode) at any point in time. Multiple session objects may
- /// coexists for a single file, as long as bind() has been called on at most one
- /// of them. Additionally, two bound session objects for the same file are
- /// allowed to exist at different times, if they have no overlap in time (in
- /// their bound state), as long as they are associated with the same client
- /// object, or with two different client objects that do not overlap in
- /// time. This means, in particular, that it is an error to create two bound
- /// session objects for the same local Realm file, if they are associated with
- /// two different client objects that overlap in time, even if the session
- /// objects do not overlap in time (in their bound state). It is the
- /// responsibility of the application to ensure that these rules are adhered
- /// to. The consequences of a violation are unspecified.
- ///
- /// Thread-safety: It is safe for multiple threads to construct, use (with some
- /// exceptions), and destroy session objects concurrently, regardless of whether
- /// those session objects are associated with the same, or with different Client
- /// objects. Please note that some of the public member functions are fully
- /// thread-safe, while others are not.
- ///
- /// Callback semantics: All session specific callback functions will be executed
- /// by the event loop thread, i.e., the thread that calls Client::run(). No
- /// callback function will be called before Session::bind() is called. Callback
- /// functions that are specified prior to calling bind() (e.g., any passed to
- /// set_progress_handler()) may start to execute before bind() returns, as long
- /// as some thread is executing Client::run(). Likewise, completion handlers,
- /// such as those passed to async_wait_for_sync_completion() may start to
- /// execute before the submitting function returns. All session specific
- /// callback functions (including completion handlers) are guaranteed to no
- /// longer be executing when session termination completes, and they are
- /// guaranteed to not be called after session termination completes. Termination
- /// is an event that completes asynchronously with respect to the application,
- /// but is initiated by calling detach(), or implicitly by destroying a session
- /// object. After having initiated one or more session terminations, the
- /// application can wait for those terminations to complete by calling
- /// Client::wait_for_session_terminations_or_client_stopped(). Since callback
- /// functions are always executed by the event loop thread, they are also
- /// guaranteed to not be executing after Client::run() has returned.
- class Session {
- public:
- using ErrorInfo = SessionErrorInfo;
- using port_type = sync::port_type;
- using SyncTransactCallback = void(VersionID old_version, VersionID new_version);
- using ProgressHandler = void(std::uint_fast64_t downloaded_bytes, std::uint_fast64_t downloadable_bytes,
- std::uint_fast64_t uploaded_bytes, std::uint_fast64_t uploadable_bytes,
- std::uint_fast64_t progress_version, std::uint_fast64_t snapshot_version);
- using WaitOperCompletionHandler = util::UniqueFunction<void(std::error_code)>;
- using SSLVerifyCallback = bool(const std::string& server_address, port_type server_port, const char* pem_data,
- size_t pem_size, int preverify_ok, int depth);
- struct Config {
- Config() {}
- /// server_address is the fully qualified host name, or IP address of
- /// the server.
- std::string server_address = "localhost";
- /// server_port is the port at which the server listens. If server_port
- /// is zero, the default port for the specified protocol is used. See
- /// ProtocolEnvelope for information on default ports.
- port_type server_port = 0;
- /// realm_identifier is the virtual path by which the server identifies the
- /// Realm.
- /// When connecting to the mock C++ server, this path must always be an
- /// absolute path, and must therefore always contain a leading slash (`/`).
- /// Furthermore, each segment of the virtual path must consist of one or
- /// more characters that are either alpha-numeric or in (`_`, `-`, `.`),
- /// and each segment is not allowed to equal `.` or `..`, and must not end
- /// with `.realm`, `.realm.lock`, or `.realm.management`. These rules are
- /// necessary because the C++ server currently reserves the right to use the
- /// specified path as part of the file system path of a Realm file.
- /// On the MongoDB Realm-based Sync server, virtual paths are not coupled
- /// to file system paths, and thus, these restrictions do not apply.
- std::string realm_identifier = "";
- /// The protocol used for communicating with the server. See
- /// ProtocolEnvelope.
- ProtocolEnvelope protocol_envelope = ProtocolEnvelope::realm;
- /// service_identifier is a prefix that is prepended to the realm_identifier
- /// in the HTTP GET request that initiates a sync connection. The value
- /// specified here must match with the server's expectation. Changing
- /// the value of service_identifier should be matched with a corresponding
- /// change in the C++ mock server.
- std::string service_identifier = "";
- ///
- /// DEPRECATED - Will be removed in a future release
- ///
- /// authorization_header_name is the name of the HTTP header containing
- /// the Realm access token. The value of the HTTP header is "Bearer <token>".
- /// authorization_header_name does not participate in session
- /// multiplexing partitioning.
- std::string authorization_header_name = "Authorization";
- ///
- /// DEPRECATED - Will be removed in a future release
- ///
- /// custom_http_headers is a map of custom HTTP headers. The keys of the map
- /// are HTTP header names, and the values are the corresponding HTTP
- /// header values.
- /// If "Authorization" is used as a custom header name,
- /// authorization_header_name must be set to anther value.
- std::map<std::string, std::string> custom_http_headers;
- ///
- /// DEPRECATED - Will be removed in a future release
- ///
- /// Controls whether the server certificate is verified for SSL
- /// connections. It should generally be true in production.
- bool verify_servers_ssl_certificate = true;
- ///
- /// DEPRECATED - Will be removed in a future release
- ///
- /// ssl_trust_certificate_path is the path of a trust/anchor
- /// certificate used by the client to verify the server certificate.
- /// ssl_trust_certificate_path is only used if the protocol is ssl and
- /// verify_servers_ssl_certificate is true.
- ///
- /// A server certificate is verified by first checking that the
- /// certificate has a valid signature chain back to a trust/anchor
- /// certificate, and secondly checking that the server_address matches
- /// a host name contained in the certificate. The host name of the
- /// certificate is stored in either Common Name or the Alternative
- /// Subject Name (DNS section).
- ///
- /// If ssl_trust_certificate_path is None (default), ssl_verify_callback
- /// (see below) is used if set, and the default device trust/anchor
- /// store is used otherwise.
- util::Optional<std::string> ssl_trust_certificate_path;
- ///
- /// DEPRECATED - Will be removed in a future release
- ///
- /// If Client::Config::ssl_verify_callback is set, that function is called
- /// to verify the certificate, unless verify_servers_ssl_certificate is
- /// false.
- /// ssl_verify_callback is used to implement custom SSL certificate
- /// verification. it is only used if the protocol is SSL,
- /// verify_servers_ssl_certificate is true and ssl_trust_certificate_path
- /// is None.
- ///
- /// The signature of ssl_verify_callback is
- ///
- /// bool(const std::string& server_address,
- /// port_type server_port,
- /// const char* pem_data,
- /// size_t pem_size,
- /// int preverify_ok,
- /// int depth);
- ///
- /// server address and server_port is the address and port of the server
- /// that a SSL connection is being established to. They are identical to
- /// the server_address and server_port set in this config file and are
- /// passed for convenience.
- /// pem_data is the certificate of length pem_size in
- /// the PEM format. preverify_ok is OpenSSL's preverification of the
- /// certificate. preverify_ok is either 0, or 1. If preverify_ok is 1,
- /// OpenSSL has accepted the certificate and it will generally be safe
- /// to trust that certificate. depth represents the position of the
- /// certificate in the certificate chain sent by the server. depth = 0
- /// represents the actual server certificate that should contain the
- /// host name(server address) of the server. The highest depth is the
- /// root certificate.
- /// The callback function will receive the certificates starting from
- /// the root certificate and moving down the chain until it reaches the
- /// server's own certificate with a host name. The depth of the last
- /// certificate is 0. The depth of the first certificate is chain
- /// length - 1.
- ///
- /// The return value of the callback function decides whether the
- /// client accepts the certificate. If the return value is false, the
- /// processing of the certificate chain is interrupted and the SSL
- /// connection is rejected. If the return value is true, the verification
- /// process continues. If the callback function returns true for all
- /// presented certificates including the depth == 0 certificate, the
- /// SSL connection is accepted.
- ///
- /// A recommended way of using the callback function is to return true
- /// if preverify_ok = 1 and depth > 0,
- /// always check the host name if depth = 0,
- /// and use an independent verification step if preverify_ok = 0.
- ///
- /// Another possible way of using the callback is to collect all the
- /// certificates until depth = 0, and present the entire chain for
- /// independent verification.
- std::function<SSLVerifyCallback> ssl_verify_callback;
- /// signed_user_token is a cryptographically signed token describing the
- /// identity and access rights of the current user.
- std::string signed_user_token;
- using ClientReset = sync::ClientReset;
- util::Optional<ClientReset> client_reset_config;
- ///
- /// DEPRECATED - Will be removed in a future release
- ///
- util::Optional<SyncConfig::ProxyConfig> proxy_config;
- /// When integrating a flexible sync bootstrap, process this many bytes of
- /// changeset data in a single integration attempt.
- size_t flx_bootstrap_batch_size_bytes = 1024 * 1024;
- /// Set to true to cause the integration of the first received changeset
- /// (in a DOWNLOAD message) to fail.
- ///
- /// This feature exists exclusively for testing purposes at this time.
- bool simulate_integration_error = false;
- std::function<SyncClientHookAction(const SyncClientHookData&)> on_sync_client_event_hook;
- };
- /// \brief Start a new session for the specified client-side Realm.
- ///
- /// Note that the session is not fully activated until you call bind().
- /// Also note that if you call set_sync_transact_callback(), it must be
- /// done before calling bind().
- Session(Client&, std::shared_ptr<DB>, std::shared_ptr<SubscriptionStore>, Config&& = {});
- /// This leaves the right-hand side session object detached. See "Thread
- /// safety" section under detach().
- Session(Session&&) noexcept;
- /// Create a detached session object (see detach()).
- Session() noexcept = default;
- /// Implies detachment. See "Thread safety" section under detach().
- ~Session() noexcept;
- /// Detach the object on the left-hand side, then "steal" the session from
- /// the object on the right-hand side, if there is one. This leaves the
- /// object on the right-hand side detached. See "Thread safety" section
- /// under detach().
- Session& operator=(Session&&) noexcept;
- /// Detach this session object from the client object (Client). If the
- /// session object is already detached, this function has no effect
- /// (idempotency).
- ///
- /// Detachment initiates session termination, which is an event that takes
- /// place shortly thereafter in the context of the client's event loop
- /// thread.
- ///
- /// A detached session object may be destroyed, move-assigned to, and moved
- /// from. Apart from that, it is an error to call any function other than
- /// detach() on a detached session object.
- ///
- /// Thread safety: Detachment is not a thread-safe operation. This means
- /// that detach() may not be executed by two threads concurrently, and may
- /// not execute concurrently with object destruction. Additionally,
- /// detachment must not execute concurrently with a moving operation
- /// involving the session object on the left or right-hand side. See move
- /// constructor and assignment operator.
- void detach() noexcept;
- /// \brief Set a function to be called when the local Realm has changed due
- /// to integration of a downloaded changeset.
- ///
- /// Specify the callback function that will be called when one or more
- /// transactions are performed to integrate downloaded changesets into the
- /// client-side Realm, that is associated with this session.
- ///
- /// The callback function will always be called by the thread that executes
- /// the event loop (Client::run()), but not until bind() is called. If the
- /// callback function throws an exception, that exception will "travel" out
- /// through Client::run().
- ///
- /// Note: Any call to this function must have returned before bind() is
- /// called. If this function is called multiple times, each call overrides
- /// the previous setting.
- ///
- /// Note: This function is **not thread-safe**. That is, it is an error if
- /// it is called while another thread is executing any member function on
- /// the same Session object.
- ///
- /// CAUTION: The specified callback function may get called before the call
- /// to bind() returns, and it may get called (or continue to execute) after
- /// the session object is destroyed. Please see "Callback semantics" section
- /// under Session for more on this.
- void set_sync_transact_callback(util::UniqueFunction<SyncTransactCallback>);
- /// \brief Set a handler to monitor the state of download and upload
- /// progress.
- ///
- /// The handler must have signature
- ///
- /// void(uint_fast64_t downloaded_bytes, uint_fast64_t downloadable_bytes,
- /// uint_fast64_t uploaded_bytes, uint_fast64_t uploadable_bytes,
- /// uint_fast64_t progress_version);
- ///
- /// downloaded_bytes is the size in bytes of all downloaded changesets.
- /// downloadable_bytes is equal to downloaded_bytes plus an estimate of
- /// the size of the remaining server history.
- ///
- /// uploaded_bytes is the size in bytes of all locally produced changesets
- /// that have been received and acknowledged by the server.
- /// uploadable_bytes is the size in bytes of all locally produced changesets.
- ///
- /// Due to the nature of the merge rules, it is possible that the size of an
- /// uploaded changeset uploaded from one client is not equal to the size of
- /// the changesets that other clients will download.
- ///
- /// Typical uses of this function:
- ///
- /// Upload completion can be checked by
- ///
- /// bool upload_complete = (uploaded_bytes == uploadable_bytes);
- ///
- /// Download completion could be checked by
- ///
- /// bool download_complete = (downloaded_bytes == downloadable_bytes);
- ///
- /// However, download completion might never be reached because the server
- /// can receive new changesets from other clients. downloadable_bytes can
- /// decrease for two reasons: server side compaction and changesets of
- /// local origin. Code using downloadable_bytes must not assume that it
- /// is increasing.
- ///
- /// Upload progress can be calculated by caching an initial value of
- /// uploaded_bytes from the last, or next, callback. Then
- ///
- /// double upload_progress =
- /// (uploaded_bytes - initial_uploaded_bytes)
- /// -------------------------------------------
- /// (uploadable_bytes - initial_uploaded_bytes)
- ///
- /// Download progress can be calculates similarly:
- ///
- /// double download_progress =
- /// (downloaded_bytes - initial_downloaded_bytes)
- /// -----------------------------------------------
- /// (downloadable_bytes - initial_downloaded_bytes)
- ///
- /// progress_version is 0 at the start of a session. When at least one
- /// DOWNLOAD message has been received from the server, progress_version is
- /// positive. progress_version can be used to ensure that the reported
- /// progress contains information obtained from the server in the current
- /// session. The server will send a message as soon as possible, and the
- /// progress handler will eventually be called with a positive progress_version
- /// unless the session is interrupted before a message from the server has
- /// been received.
- ///
- /// The handler is called on the event loop thread.The handler after bind(),
- /// after each DOWNLOAD message, and after each local transaction
- /// (nonsync_transact_notify).
- ///
- /// set_progress_handler() is not thread safe and it must be called before
- /// bind() is called. Subsequent calls to set_progress_handler() overwrite
- /// the previous calls. Typically, this function is called once per session.
- ///
- /// CAUTION: The specified callback function may get called before the call
- /// to bind() returns, and it may get called (or continue to execute) after
- /// the session object is destroyed. Please see "Callback semantics" section
- /// under Session for more on this.
- void set_progress_handler(util::UniqueFunction<ProgressHandler>);
- using ConnectionStateChangeListener = void(ConnectionState, util::Optional<SessionErrorInfo>);
- /// \brief Install a connection state change listener.
- ///
- /// Sets a function to be called whenever the state of the underlying
- /// network connection changes between "disconnected", "connecting", and
- /// "connected". The initial state is always "disconnected". The next state
- /// after "disconnected" is always "connecting". The next state after
- /// "connecting" is either "connected" or "disconnected". The next state
- /// after "connected" is always "disconnected". A switch to the
- /// "disconnected" state only happens when an error occurs.
- ///
- /// Whenever the installed function is called, an SessionErrorInfo object is passed
- /// when, and only when the passed state is ConnectionState::disconnected.
- ///
- /// When multiple sessions share a single connection, the state changes will
- /// be reported for each session in turn.
- ///
- /// The callback function will always be called by the thread that executes
- /// the event loop (Client::run()), but not until bind() is called. If the
- /// callback function throws an exception, that exception will "travel" out
- /// through Client::run().
- ///
- /// Note: Any call to this function must have returned before bind() is
- /// called. If this function is called multiple times, each call overrides
- /// the previous setting.
- ///
- /// Note: This function is **not thread-safe**. That is, it is an error if
- /// it is called while another thread is executing any member function on
- /// the same Session object.
- ///
- /// CAUTION: The specified callback function may get called before the call
- /// to bind() returns, and it may get called (or continue to execute) after
- /// the session object is destroyed. Please see "Callback semantics" section
- /// under Session for more on this.
- void set_connection_state_change_listener(util::UniqueFunction<ConnectionStateChangeListener>);
- //@{
- /// Deprecated! Use set_connection_state_change_listener() instead.
- using ErrorHandler = void(const SessionErrorInfo&);
- void set_error_handler(util::UniqueFunction<ErrorHandler>);
- //@}
- /// @{ \brief Bind this session to the specified server side Realm.
- ///
- /// No communication takes place on behalf of this session before the
- /// session is bound, but as soon as the session becomes bound, the server
- /// will start to push changes to the client, and vice versa.
- ///
- /// If a callback function was set using set_sync_transact_callback(), then
- /// that callback function will start to be called as changesets are
- /// downloaded and integrated locally. It is important to understand that
- /// callback functions are executed by the event loop thread (Client::run())
- /// and the callback function may therefore be called before bind() returns.
- ///
- /// Note: It is an error if this function is called more than once per
- /// Session object.
- ///
- /// Note: This function is **not thread-safe**. That is, it is an error if
- /// it is called while another thread is executing any member function on
- /// the same Session object.
- ///
- /// bind() binds this session to the specified server side Realm using the
- /// parameters specified in the Session::Config object.
- ///
- /// The two other forms of bind() are convenience functions.
- void bind();
- /// @}
- /// \brief Refresh the access token associated with this session.
- ///
- /// This causes the REFRESH protocol message to be sent to the server. See
- /// ProtocolEnvelope. It is an error to pass a token with a different user
- /// identity than the token used to initiate the session.
- ///
- /// In an on-going session the application may expect the access token to
- /// expire at a certain time and schedule acquisition of a fresh access
- /// token (using a refresh token or by other means) in due time to provide a
- /// better user experience, and seamless connectivity to the server.
- ///
- /// If the application does not proactively refresh an expiring token, the
- /// session will eventually be disconnected. The application can detect this
- /// by monitoring the connection state
- /// (set_connection_state_change_listener()), and check whether the error
- /// code is `ProtocolError::token_expired`. Such a session can then be
- /// revived by calling refresh() with a newly acquired access token.
- ///
- /// Due to protocol techicalities, a race condition exists that can cause a
- /// session to become, and remain disconnected after a new access token has
- /// been passed to refresh(). The application can work around this race
- /// condition by detecting the `ProtocolError::token_expired` error, and
- /// always initiate a token renewal in this case.
- ///
- /// It is an error to call this function before calling `Client::bind()`.
- ///
- /// Note: This function is thread-safe.
- ///
- /// \param signed_user_token A cryptographically signed token describing the
- /// identity and access rights of the current user. See ProtocolEnvelope.
- void refresh(const std::string& signed_user_token);
- /// \brief Inform the synchronization agent about changes of local origin.
- ///
- /// This function must be called by the application after a transaction
- /// performed on its behalf, that is, after a transaction that is not
- /// performed to integrate a changeset that was downloaded from the server.
- ///
- /// It is an error to call this function before bind() has been called, and
- /// has returned.
- ///
- /// Note: This function is fully thread-safe. That is, it may be called by
- /// any thread, and by multiple threads concurrently.
- void nonsync_transact_notify(version_type new_version);
- /// @{ \brief Wait for upload, download, or upload+download completion.
- ///
- /// async_wait_for_upload_completion() initiates an asynchronous wait for
- /// upload to complete, async_wait_for_download_completion() initiates an
- /// asynchronous wait for download to complete, and
- /// async_wait_for_sync_completion() initiates an asynchronous wait for
- /// upload and download to complete.
- ///
- /// Upload is considered complete when all non-empty changesets of local
- /// origin have been uploaded to the server, and the server has acknowledged
- /// reception of them. Changesets of local origin introduced after the
- /// initiation of the session (after bind() is called) will generally not be
- /// considered for upload unless they are announced to this client through
- /// nonsync_transact_notify() prior to the initiation of the wait operation,
- /// i.e., prior to the invocation of async_wait_for_upload_completion() or
- /// async_wait_for_sync_completion(). Unannounced changesets may get picked
- /// up, but there is no guarantee that they will be, however, if a certain
- /// changeset is announced, then all previous changesets are implicitly
- /// announced. Also all preexisting changesets are implicitly announced
- /// when the session is initiated.
- ///
- /// Download is considered complete when all non-empty changesets of remote
- /// origin have been downloaded from the server, and integrated into the
- /// local Realm state. To know what is currently outstanding on the server,
- /// the client always sends a special "marker" message to the server, and
- /// waits until it has downloaded all outstanding changesets that were
- /// present on the server at the time when the server received that marker
- /// message. Each call to async_wait_for_download_completion() and
- /// async_wait_for_sync_completion() therefore requires a full client <->
- /// server round-trip.
- ///
- /// If a new wait operation is initiated while another wait operation is in
- /// progress by another thread, the waiting period of first operation may,
- /// or may not get extended. The application must not assume either. The
- /// application may assume, however, that async_wait_for_upload_completion()
- /// will not affect the waiting period of
- /// async_wait_for_download_completion(), and vice versa.
- ///
- /// It is an error to call these functions before bind() has been called,
- /// and has returned.
- ///
- /// The specified completion handlers will always be executed by the thread
- /// that executes the event loop (the thread that calls Client::run()). If
- /// the handler throws an exception, that exception will "travel" out
- /// through Client::run().
- ///
- /// If incomplete wait operations exist when the session is terminated,
- /// those wait operations will be canceled. Session termination is an event
- /// that happens in the context of the client's event loop thread shortly
- /// after the destruction of the session object. The std::error_code
- /// argument passed to the completion handler of a canceled wait operation
- /// will be `util::error::operation_aborted`. For uncanceled wait operations
- /// it will be `std::error_code{}`. Note that as long as the client's event
- /// loop thread is running, all completion handlers will be called
- /// regardless of whether the operations get canceled or not.
- ///
- /// CAUTION: The specified completion handlers may get called before the
- /// call to the waiting function returns, and it may get called (or continue
- /// to execute) after the session object is destroyed. Please see "Callback
- /// semantics" section under Session for more on this.
- ///
- /// Note: These functions are fully thread-safe. That is, they may be called
- /// by any thread, and by multiple threads concurrently.
- void async_wait_for_sync_completion(WaitOperCompletionHandler);
- void async_wait_for_upload_completion(WaitOperCompletionHandler);
- void async_wait_for_download_completion(WaitOperCompletionHandler);
- /// @}
- /// @{ \brief Synchronous wait for upload or download completion.
- ///
- /// These functions are synchronous equivalents of
- /// async_wait_for_upload_completion() and
- /// async_wait_for_download_completion() respectively. This means that they
- /// block the caller until the completion condition is satisfied, or the
- /// client's event loop thread exits from Client::run(), whichever happens
- /// first.
- ///
- /// It is an error to call these functions before bind() has been called,
- /// and has returned.
- ///
- /// CAUTION: If Client::run() returns while a wait operation is in progress,
- /// these waiting functions return immediately, even if the completion
- /// condition is not yet satisfied. The completion condition is guaranteed
- /// to be satisfied only when these functions return true.
- ///
- /// \return True only if the completion condition was satisfied. False if
- /// the client's event loop thread exited from Client::run() in which case
- /// the completion condition may, or may not have been satisfied.
- ///
- /// Note: These functions are fully thread-safe. That is, they may be called
- /// by any thread, and by multiple threads concurrently.
- bool wait_for_upload_complete_or_client_stopped();
- bool wait_for_download_complete_or_client_stopped();
- /// @}
- /// \brief Cancel the current or next reconnect delay for the server
- /// associated with this session.
- ///
- /// When the network connection is severed, or an attempt to establish
- /// connection fails, a certain delay will take effect before the client
- /// will attempt to reestablish the connection. This delay will generally
- /// grow with the number of unsuccessful reconnect attempts, and can grow to
- /// over a minute. In some cases however, the application will know when it
- /// is a good time to stop waiting and retry immediately. One example is
- /// when a device has been offline for a while, and the operating system
- /// then tells the application that network connectivity has been restored.
- ///
- /// Clearly, this function should not be called too often and over extended
- /// periods of time, as that would effectively disable the built-in "server
- /// hammering" protection.
- ///
- /// It is an error to call this function before bind() has been called, and
- /// has returned.
- ///
- /// This function is fully thread-safe. That is, it may be called by any
- /// thread, and by multiple threads concurrently.
- void cancel_reconnect_delay();
- void on_new_flx_sync_subscription(int64_t new_version);
- util::Future<std::string> send_test_command(std::string command_body);
- private:
- SessionWrapper* m_impl = nullptr;
- void abandon() noexcept;
- void async_wait_for(bool upload_completion, bool download_completion, WaitOperCompletionHandler);
- };
- std::ostream& operator<<(std::ostream& os, SyncConfig::ProxyConfig::Type);
- // Implementation
- class BadServerUrl : public Exception {
- public:
- BadServerUrl(std::string_view url)
- : Exception(ErrorCodes::BadServerUrl, util::format("Unable to parse server URL '%1'", url))
- {
- }
- };
- inline Session::Session(Session&& sess) noexcept
- : m_impl{sess.m_impl}
- {
- sess.m_impl = nullptr;
- }
- inline Session::~Session() noexcept
- {
- if (m_impl)
- abandon();
- }
- inline Session& Session::operator=(Session&& sess) noexcept
- {
- if (m_impl)
- abandon();
- m_impl = sess.m_impl;
- sess.m_impl = nullptr;
- return *this;
- }
- inline void Session::detach() noexcept
- {
- if (m_impl)
- abandon();
- m_impl = nullptr;
- }
- inline void Session::set_error_handler(util::UniqueFunction<ErrorHandler> handler)
- {
- auto handler_2 = [handler = std::move(handler)](ConnectionState state,
- const util::Optional<SessionErrorInfo>& error_info) {
- if (state != ConnectionState::disconnected)
- return;
- REALM_ASSERT(error_info);
- handler(*error_info); // Throws
- };
- set_connection_state_change_listener(std::move(handler_2)); // Throws
- }
- inline void Session::async_wait_for_sync_completion(WaitOperCompletionHandler handler)
- {
- bool upload_completion = true, download_completion = true;
- async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
- }
- inline void Session::async_wait_for_upload_completion(WaitOperCompletionHandler handler)
- {
- bool upload_completion = true, download_completion = false;
- async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
- }
- inline void Session::async_wait_for_download_completion(WaitOperCompletionHandler handler)
- {
- bool upload_completion = false, download_completion = true;
- async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
- }
- } // namespace realm::sync
- #endif // REALM_SYNC_CLIENT_HPP
|