client.hpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. #ifndef REALM_SYNC_CLIENT_HPP
  2. #define REALM_SYNC_CLIENT_HPP
  3. #include <cstddef>
  4. #include <cstdint>
  5. #include <memory>
  6. #include <utility>
  7. #include <functional>
  8. #include <exception>
  9. #include <string>
  10. #include <realm/util/buffer.hpp>
  11. #include <realm/util/functional.hpp>
  12. #include <realm/sync/client_base.hpp>
  13. #include <realm/sync/socket_provider.hpp>
  14. #include <realm/sync/subscriptions.hpp>
  15. namespace realm::sync {
  16. class Client {
  17. public:
  18. using port_type = sync::port_type;
  19. using Error = ClientError;
  20. static constexpr milliseconds_type default_connect_timeout = sync::default_connect_timeout;
  21. static constexpr milliseconds_type default_connection_linger_time = sync::default_connection_linger_time;
  22. static constexpr milliseconds_type default_ping_keepalive_period = sync::default_ping_keepalive_period;
  23. static constexpr milliseconds_type default_pong_keepalive_timeout = sync::default_pong_keepalive_timeout;
  24. static constexpr milliseconds_type default_fast_reconnect_limit = sync::default_fast_reconnect_limit;
  25. using Config = ClientConfig;
  26. /// \throw util::EventLoop::Implementation::NotAvailable if no event loop
  27. /// implementation was specified, and
  28. /// util::EventLoop::Implementation::get_default() throws it.
  29. Client(Config = {});
  30. Client(Client&&) noexcept;
  31. ~Client() noexcept;
  32. /// Run the internal event-loop of the client. At most one thread may
  33. /// execute run() at any given time. The call will not return until somebody
  34. /// calls stop().
  35. void run() noexcept;
  36. /// See run().
  37. ///
  38. /// Thread-safe.
  39. void shutdown() noexcept;
  40. /// Forces all connections to close and waits for any pending work on the event
  41. /// loop to complete. All sessions must be destroyed before calling shutdown_and_wait.
  42. void shutdown_and_wait();
  43. /// \brief Cancel current or next reconnect delay for all servers.
  44. ///
  45. /// This corresponds to calling Session::cancel_reconnect_delay() on all
  46. /// bound sessions, but will also cancel reconnect delays applying to
  47. /// servers for which there are currently no bound sessions.
  48. ///
  49. /// Thread-safe.
  50. void cancel_reconnect_delay();
  51. /// \brief Wait for session termination to complete.
  52. ///
  53. /// Wait for termination of all sessions whose termination was initiated
  54. /// prior this call (the completion condition), or until the client's event
  55. /// loop thread exits from Client::run(), whichever happens
  56. /// first. Termination of a session can be initiated implicitly (e.g., via
  57. /// destruction of the session object), or explicitly by Session::detach().
  58. ///
  59. /// Note: After session termination (when this function returns true) no
  60. /// session specific callback function can be called or continue to execute,
  61. /// and the client is guaranteed to no longer have a Realm file open on
  62. /// behalf of the terminated session.
  63. ///
  64. /// CAUTION: If run() returns while a wait operation is in progress, this
  65. /// waiting function will return immediately, even if the completion
  66. /// condition is not yet satisfied. The completion condition is guaranteed
  67. /// to be satisfied only when these functions return true. If it returns
  68. /// false, session specific callback functions may still be executing or get
  69. /// called, and the associated Realm files may still not have been closed.
  70. ///
  71. /// If a new wait operation is initiated while another wait operation is in
  72. /// progress by another thread, the waiting period of fist operation may, or
  73. /// may not get extended. The application must not assume either.
  74. ///
  75. /// Note: Session termination does not imply that the client has received an
  76. /// UNBOUND message from the server (see the protocol specification). This
  77. /// may happen later.
  78. ///
  79. /// \return True only if the completion condition was satisfied. False if
  80. /// the client's event loop thread exited from Client::run() in which case
  81. /// the completion condition may, or may not have been satisfied.
  82. ///
  83. /// Note: These functions are fully thread-safe. That is, they may be called
  84. /// by any thread, and by multiple threads concurrently.
  85. bool wait_for_session_terminations_or_client_stopped();
  86. /// Returns false if the specified URL is invalid.
  87. bool decompose_server_url(const std::string& url, ProtocolEnvelope& protocol, std::string& address,
  88. port_type& port, std::string& path) const;
  89. private:
  90. std::unique_ptr<ClientImpl> m_impl;
  91. friend class Session;
  92. };
  93. class BadServerUrl; // Exception
  94. /// \brief Client-side representation of a Realm file synchronization session.
  95. ///
  96. /// A synchronization session deals with precisely one local Realm file. To
  97. /// synchronize multiple local Realm files, you need multiple sessions.
  98. ///
  99. /// A session object is always associated with a particular client object (\ref
  100. /// Client). The application must ensure that the destruction of the associated
  101. /// client object never happens before the destruction of the session
  102. /// object. The consequences of a violation are unspecified.
  103. ///
  104. /// A session object is always associated with a particular local Realm file,
  105. /// however, a session object does not represent a session until it is bound to
  106. /// a server side Realm, i.e., until bind() is called. From the point of view of
  107. /// the thread that calls bind(), the session starts precisely when the
  108. /// execution of bind() starts, i.e., before bind() returns.
  109. ///
  110. /// At most one session is allowed to exist for a particular local Realm file
  111. /// (file system inode) at any point in time. Multiple session objects may
  112. /// coexists for a single file, as long as bind() has been called on at most one
  113. /// of them. Additionally, two bound session objects for the same file are
  114. /// allowed to exist at different times, if they have no overlap in time (in
  115. /// their bound state), as long as they are associated with the same client
  116. /// object, or with two different client objects that do not overlap in
  117. /// time. This means, in particular, that it is an error to create two bound
  118. /// session objects for the same local Realm file, if they are associated with
  119. /// two different client objects that overlap in time, even if the session
  120. /// objects do not overlap in time (in their bound state). It is the
  121. /// responsibility of the application to ensure that these rules are adhered
  122. /// to. The consequences of a violation are unspecified.
  123. ///
  124. /// Thread-safety: It is safe for multiple threads to construct, use (with some
  125. /// exceptions), and destroy session objects concurrently, regardless of whether
  126. /// those session objects are associated with the same, or with different Client
  127. /// objects. Please note that some of the public member functions are fully
  128. /// thread-safe, while others are not.
  129. ///
  130. /// Callback semantics: All session specific callback functions will be executed
  131. /// by the event loop thread, i.e., the thread that calls Client::run(). No
  132. /// callback function will be called before Session::bind() is called. Callback
  133. /// functions that are specified prior to calling bind() (e.g., any passed to
  134. /// set_progress_handler()) may start to execute before bind() returns, as long
  135. /// as some thread is executing Client::run(). Likewise, completion handlers,
  136. /// such as those passed to async_wait_for_sync_completion() may start to
  137. /// execute before the submitting function returns. All session specific
  138. /// callback functions (including completion handlers) are guaranteed to no
  139. /// longer be executing when session termination completes, and they are
  140. /// guaranteed to not be called after session termination completes. Termination
  141. /// is an event that completes asynchronously with respect to the application,
  142. /// but is initiated by calling detach(), or implicitly by destroying a session
  143. /// object. After having initiated one or more session terminations, the
  144. /// application can wait for those terminations to complete by calling
  145. /// Client::wait_for_session_terminations_or_client_stopped(). Since callback
  146. /// functions are always executed by the event loop thread, they are also
  147. /// guaranteed to not be executing after Client::run() has returned.
  148. class Session {
  149. public:
  150. using ErrorInfo = SessionErrorInfo;
  151. using port_type = sync::port_type;
  152. using SyncTransactCallback = void(VersionID old_version, VersionID new_version);
  153. using ProgressHandler = void(std::uint_fast64_t downloaded_bytes, std::uint_fast64_t downloadable_bytes,
  154. std::uint_fast64_t uploaded_bytes, std::uint_fast64_t uploadable_bytes,
  155. std::uint_fast64_t progress_version, std::uint_fast64_t snapshot_version);
  156. using WaitOperCompletionHandler = util::UniqueFunction<void(std::error_code)>;
  157. using SSLVerifyCallback = bool(const std::string& server_address, port_type server_port, const char* pem_data,
  158. size_t pem_size, int preverify_ok, int depth);
  159. struct Config {
  160. Config() {}
  161. /// server_address is the fully qualified host name, or IP address of
  162. /// the server.
  163. std::string server_address = "localhost";
  164. /// server_port is the port at which the server listens. If server_port
  165. /// is zero, the default port for the specified protocol is used. See
  166. /// ProtocolEnvelope for information on default ports.
  167. port_type server_port = 0;
  168. /// realm_identifier is the virtual path by which the server identifies the
  169. /// Realm.
  170. /// When connecting to the mock C++ server, this path must always be an
  171. /// absolute path, and must therefore always contain a leading slash (`/`).
  172. /// Furthermore, each segment of the virtual path must consist of one or
  173. /// more characters that are either alpha-numeric or in (`_`, `-`, `.`),
  174. /// and each segment is not allowed to equal `.` or `..`, and must not end
  175. /// with `.realm`, `.realm.lock`, or `.realm.management`. These rules are
  176. /// necessary because the C++ server currently reserves the right to use the
  177. /// specified path as part of the file system path of a Realm file.
  178. /// On the MongoDB Realm-based Sync server, virtual paths are not coupled
  179. /// to file system paths, and thus, these restrictions do not apply.
  180. std::string realm_identifier = "";
  181. /// The protocol used for communicating with the server. See
  182. /// ProtocolEnvelope.
  183. ProtocolEnvelope protocol_envelope = ProtocolEnvelope::realm;
  184. /// service_identifier is a prefix that is prepended to the realm_identifier
  185. /// in the HTTP GET request that initiates a sync connection. The value
  186. /// specified here must match with the server's expectation. Changing
  187. /// the value of service_identifier should be matched with a corresponding
  188. /// change in the C++ mock server.
  189. std::string service_identifier = "";
  190. ///
  191. /// DEPRECATED - Will be removed in a future release
  192. ///
  193. /// authorization_header_name is the name of the HTTP header containing
  194. /// the Realm access token. The value of the HTTP header is "Bearer <token>".
  195. /// authorization_header_name does not participate in session
  196. /// multiplexing partitioning.
  197. std::string authorization_header_name = "Authorization";
  198. ///
  199. /// DEPRECATED - Will be removed in a future release
  200. ///
  201. /// custom_http_headers is a map of custom HTTP headers. The keys of the map
  202. /// are HTTP header names, and the values are the corresponding HTTP
  203. /// header values.
  204. /// If "Authorization" is used as a custom header name,
  205. /// authorization_header_name must be set to anther value.
  206. std::map<std::string, std::string> custom_http_headers;
  207. ///
  208. /// DEPRECATED - Will be removed in a future release
  209. ///
  210. /// Controls whether the server certificate is verified for SSL
  211. /// connections. It should generally be true in production.
  212. bool verify_servers_ssl_certificate = true;
  213. ///
  214. /// DEPRECATED - Will be removed in a future release
  215. ///
  216. /// ssl_trust_certificate_path is the path of a trust/anchor
  217. /// certificate used by the client to verify the server certificate.
  218. /// ssl_trust_certificate_path is only used if the protocol is ssl and
  219. /// verify_servers_ssl_certificate is true.
  220. ///
  221. /// A server certificate is verified by first checking that the
  222. /// certificate has a valid signature chain back to a trust/anchor
  223. /// certificate, and secondly checking that the server_address matches
  224. /// a host name contained in the certificate. The host name of the
  225. /// certificate is stored in either Common Name or the Alternative
  226. /// Subject Name (DNS section).
  227. ///
  228. /// If ssl_trust_certificate_path is None (default), ssl_verify_callback
  229. /// (see below) is used if set, and the default device trust/anchor
  230. /// store is used otherwise.
  231. util::Optional<std::string> ssl_trust_certificate_path;
  232. ///
  233. /// DEPRECATED - Will be removed in a future release
  234. ///
  235. /// If Client::Config::ssl_verify_callback is set, that function is called
  236. /// to verify the certificate, unless verify_servers_ssl_certificate is
  237. /// false.
  238. /// ssl_verify_callback is used to implement custom SSL certificate
  239. /// verification. it is only used if the protocol is SSL,
  240. /// verify_servers_ssl_certificate is true and ssl_trust_certificate_path
  241. /// is None.
  242. ///
  243. /// The signature of ssl_verify_callback is
  244. ///
  245. /// bool(const std::string& server_address,
  246. /// port_type server_port,
  247. /// const char* pem_data,
  248. /// size_t pem_size,
  249. /// int preverify_ok,
  250. /// int depth);
  251. ///
  252. /// server address and server_port is the address and port of the server
  253. /// that a SSL connection is being established to. They are identical to
  254. /// the server_address and server_port set in this config file and are
  255. /// passed for convenience.
  256. /// pem_data is the certificate of length pem_size in
  257. /// the PEM format. preverify_ok is OpenSSL's preverification of the
  258. /// certificate. preverify_ok is either 0, or 1. If preverify_ok is 1,
  259. /// OpenSSL has accepted the certificate and it will generally be safe
  260. /// to trust that certificate. depth represents the position of the
  261. /// certificate in the certificate chain sent by the server. depth = 0
  262. /// represents the actual server certificate that should contain the
  263. /// host name(server address) of the server. The highest depth is the
  264. /// root certificate.
  265. /// The callback function will receive the certificates starting from
  266. /// the root certificate and moving down the chain until it reaches the
  267. /// server's own certificate with a host name. The depth of the last
  268. /// certificate is 0. The depth of the first certificate is chain
  269. /// length - 1.
  270. ///
  271. /// The return value of the callback function decides whether the
  272. /// client accepts the certificate. If the return value is false, the
  273. /// processing of the certificate chain is interrupted and the SSL
  274. /// connection is rejected. If the return value is true, the verification
  275. /// process continues. If the callback function returns true for all
  276. /// presented certificates including the depth == 0 certificate, the
  277. /// SSL connection is accepted.
  278. ///
  279. /// A recommended way of using the callback function is to return true
  280. /// if preverify_ok = 1 and depth > 0,
  281. /// always check the host name if depth = 0,
  282. /// and use an independent verification step if preverify_ok = 0.
  283. ///
  284. /// Another possible way of using the callback is to collect all the
  285. /// certificates until depth = 0, and present the entire chain for
  286. /// independent verification.
  287. std::function<SSLVerifyCallback> ssl_verify_callback;
  288. /// signed_user_token is a cryptographically signed token describing the
  289. /// identity and access rights of the current user.
  290. std::string signed_user_token;
  291. using ClientReset = sync::ClientReset;
  292. util::Optional<ClientReset> client_reset_config;
  293. ///
  294. /// DEPRECATED - Will be removed in a future release
  295. ///
  296. util::Optional<SyncConfig::ProxyConfig> proxy_config;
  297. /// When integrating a flexible sync bootstrap, process this many bytes of
  298. /// changeset data in a single integration attempt.
  299. size_t flx_bootstrap_batch_size_bytes = 1024 * 1024;
  300. /// Set to true to cause the integration of the first received changeset
  301. /// (in a DOWNLOAD message) to fail.
  302. ///
  303. /// This feature exists exclusively for testing purposes at this time.
  304. bool simulate_integration_error = false;
  305. std::function<SyncClientHookAction(const SyncClientHookData&)> on_sync_client_event_hook;
  306. };
  307. /// \brief Start a new session for the specified client-side Realm.
  308. ///
  309. /// Note that the session is not fully activated until you call bind().
  310. /// Also note that if you call set_sync_transact_callback(), it must be
  311. /// done before calling bind().
  312. Session(Client&, std::shared_ptr<DB>, std::shared_ptr<SubscriptionStore>, Config&& = {});
  313. /// This leaves the right-hand side session object detached. See "Thread
  314. /// safety" section under detach().
  315. Session(Session&&) noexcept;
  316. /// Create a detached session object (see detach()).
  317. Session() noexcept = default;
  318. /// Implies detachment. See "Thread safety" section under detach().
  319. ~Session() noexcept;
  320. /// Detach the object on the left-hand side, then "steal" the session from
  321. /// the object on the right-hand side, if there is one. This leaves the
  322. /// object on the right-hand side detached. See "Thread safety" section
  323. /// under detach().
  324. Session& operator=(Session&&) noexcept;
  325. /// Detach this session object from the client object (Client). If the
  326. /// session object is already detached, this function has no effect
  327. /// (idempotency).
  328. ///
  329. /// Detachment initiates session termination, which is an event that takes
  330. /// place shortly thereafter in the context of the client's event loop
  331. /// thread.
  332. ///
  333. /// A detached session object may be destroyed, move-assigned to, and moved
  334. /// from. Apart from that, it is an error to call any function other than
  335. /// detach() on a detached session object.
  336. ///
  337. /// Thread safety: Detachment is not a thread-safe operation. This means
  338. /// that detach() may not be executed by two threads concurrently, and may
  339. /// not execute concurrently with object destruction. Additionally,
  340. /// detachment must not execute concurrently with a moving operation
  341. /// involving the session object on the left or right-hand side. See move
  342. /// constructor and assignment operator.
  343. void detach() noexcept;
  344. /// \brief Set a function to be called when the local Realm has changed due
  345. /// to integration of a downloaded changeset.
  346. ///
  347. /// Specify the callback function that will be called when one or more
  348. /// transactions are performed to integrate downloaded changesets into the
  349. /// client-side Realm, that is associated with this session.
  350. ///
  351. /// The callback function will always be called by the thread that executes
  352. /// the event loop (Client::run()), but not until bind() is called. If the
  353. /// callback function throws an exception, that exception will "travel" out
  354. /// through Client::run().
  355. ///
  356. /// Note: Any call to this function must have returned before bind() is
  357. /// called. If this function is called multiple times, each call overrides
  358. /// the previous setting.
  359. ///
  360. /// Note: This function is **not thread-safe**. That is, it is an error if
  361. /// it is called while another thread is executing any member function on
  362. /// the same Session object.
  363. ///
  364. /// CAUTION: The specified callback function may get called before the call
  365. /// to bind() returns, and it may get called (or continue to execute) after
  366. /// the session object is destroyed. Please see "Callback semantics" section
  367. /// under Session for more on this.
  368. void set_sync_transact_callback(util::UniqueFunction<SyncTransactCallback>);
  369. /// \brief Set a handler to monitor the state of download and upload
  370. /// progress.
  371. ///
  372. /// The handler must have signature
  373. ///
  374. /// void(uint_fast64_t downloaded_bytes, uint_fast64_t downloadable_bytes,
  375. /// uint_fast64_t uploaded_bytes, uint_fast64_t uploadable_bytes,
  376. /// uint_fast64_t progress_version);
  377. ///
  378. /// downloaded_bytes is the size in bytes of all downloaded changesets.
  379. /// downloadable_bytes is equal to downloaded_bytes plus an estimate of
  380. /// the size of the remaining server history.
  381. ///
  382. /// uploaded_bytes is the size in bytes of all locally produced changesets
  383. /// that have been received and acknowledged by the server.
  384. /// uploadable_bytes is the size in bytes of all locally produced changesets.
  385. ///
  386. /// Due to the nature of the merge rules, it is possible that the size of an
  387. /// uploaded changeset uploaded from one client is not equal to the size of
  388. /// the changesets that other clients will download.
  389. ///
  390. /// Typical uses of this function:
  391. ///
  392. /// Upload completion can be checked by
  393. ///
  394. /// bool upload_complete = (uploaded_bytes == uploadable_bytes);
  395. ///
  396. /// Download completion could be checked by
  397. ///
  398. /// bool download_complete = (downloaded_bytes == downloadable_bytes);
  399. ///
  400. /// However, download completion might never be reached because the server
  401. /// can receive new changesets from other clients. downloadable_bytes can
  402. /// decrease for two reasons: server side compaction and changesets of
  403. /// local origin. Code using downloadable_bytes must not assume that it
  404. /// is increasing.
  405. ///
  406. /// Upload progress can be calculated by caching an initial value of
  407. /// uploaded_bytes from the last, or next, callback. Then
  408. ///
  409. /// double upload_progress =
  410. /// (uploaded_bytes - initial_uploaded_bytes)
  411. /// -------------------------------------------
  412. /// (uploadable_bytes - initial_uploaded_bytes)
  413. ///
  414. /// Download progress can be calculates similarly:
  415. ///
  416. /// double download_progress =
  417. /// (downloaded_bytes - initial_downloaded_bytes)
  418. /// -----------------------------------------------
  419. /// (downloadable_bytes - initial_downloaded_bytes)
  420. ///
  421. /// progress_version is 0 at the start of a session. When at least one
  422. /// DOWNLOAD message has been received from the server, progress_version is
  423. /// positive. progress_version can be used to ensure that the reported
  424. /// progress contains information obtained from the server in the current
  425. /// session. The server will send a message as soon as possible, and the
  426. /// progress handler will eventually be called with a positive progress_version
  427. /// unless the session is interrupted before a message from the server has
  428. /// been received.
  429. ///
  430. /// The handler is called on the event loop thread.The handler after bind(),
  431. /// after each DOWNLOAD message, and after each local transaction
  432. /// (nonsync_transact_notify).
  433. ///
  434. /// set_progress_handler() is not thread safe and it must be called before
  435. /// bind() is called. Subsequent calls to set_progress_handler() overwrite
  436. /// the previous calls. Typically, this function is called once per session.
  437. ///
  438. /// CAUTION: The specified callback function may get called before the call
  439. /// to bind() returns, and it may get called (or continue to execute) after
  440. /// the session object is destroyed. Please see "Callback semantics" section
  441. /// under Session for more on this.
  442. void set_progress_handler(util::UniqueFunction<ProgressHandler>);
  443. using ConnectionStateChangeListener = void(ConnectionState, util::Optional<SessionErrorInfo>);
  444. /// \brief Install a connection state change listener.
  445. ///
  446. /// Sets a function to be called whenever the state of the underlying
  447. /// network connection changes between "disconnected", "connecting", and
  448. /// "connected". The initial state is always "disconnected". The next state
  449. /// after "disconnected" is always "connecting". The next state after
  450. /// "connecting" is either "connected" or "disconnected". The next state
  451. /// after "connected" is always "disconnected". A switch to the
  452. /// "disconnected" state only happens when an error occurs.
  453. ///
  454. /// Whenever the installed function is called, an SessionErrorInfo object is passed
  455. /// when, and only when the passed state is ConnectionState::disconnected.
  456. ///
  457. /// When multiple sessions share a single connection, the state changes will
  458. /// be reported for each session in turn.
  459. ///
  460. /// The callback function will always be called by the thread that executes
  461. /// the event loop (Client::run()), but not until bind() is called. If the
  462. /// callback function throws an exception, that exception will "travel" out
  463. /// through Client::run().
  464. ///
  465. /// Note: Any call to this function must have returned before bind() is
  466. /// called. If this function is called multiple times, each call overrides
  467. /// the previous setting.
  468. ///
  469. /// Note: This function is **not thread-safe**. That is, it is an error if
  470. /// it is called while another thread is executing any member function on
  471. /// the same Session object.
  472. ///
  473. /// CAUTION: The specified callback function may get called before the call
  474. /// to bind() returns, and it may get called (or continue to execute) after
  475. /// the session object is destroyed. Please see "Callback semantics" section
  476. /// under Session for more on this.
  477. void set_connection_state_change_listener(util::UniqueFunction<ConnectionStateChangeListener>);
  478. //@{
  479. /// Deprecated! Use set_connection_state_change_listener() instead.
  480. using ErrorHandler = void(const SessionErrorInfo&);
  481. void set_error_handler(util::UniqueFunction<ErrorHandler>);
  482. //@}
  483. /// @{ \brief Bind this session to the specified server side Realm.
  484. ///
  485. /// No communication takes place on behalf of this session before the
  486. /// session is bound, but as soon as the session becomes bound, the server
  487. /// will start to push changes to the client, and vice versa.
  488. ///
  489. /// If a callback function was set using set_sync_transact_callback(), then
  490. /// that callback function will start to be called as changesets are
  491. /// downloaded and integrated locally. It is important to understand that
  492. /// callback functions are executed by the event loop thread (Client::run())
  493. /// and the callback function may therefore be called before bind() returns.
  494. ///
  495. /// Note: It is an error if this function is called more than once per
  496. /// Session object.
  497. ///
  498. /// Note: This function is **not thread-safe**. That is, it is an error if
  499. /// it is called while another thread is executing any member function on
  500. /// the same Session object.
  501. ///
  502. /// bind() binds this session to the specified server side Realm using the
  503. /// parameters specified in the Session::Config object.
  504. ///
  505. /// The two other forms of bind() are convenience functions.
  506. void bind();
  507. /// @}
  508. /// \brief Refresh the access token associated with this session.
  509. ///
  510. /// This causes the REFRESH protocol message to be sent to the server. See
  511. /// ProtocolEnvelope. It is an error to pass a token with a different user
  512. /// identity than the token used to initiate the session.
  513. ///
  514. /// In an on-going session the application may expect the access token to
  515. /// expire at a certain time and schedule acquisition of a fresh access
  516. /// token (using a refresh token or by other means) in due time to provide a
  517. /// better user experience, and seamless connectivity to the server.
  518. ///
  519. /// If the application does not proactively refresh an expiring token, the
  520. /// session will eventually be disconnected. The application can detect this
  521. /// by monitoring the connection state
  522. /// (set_connection_state_change_listener()), and check whether the error
  523. /// code is `ProtocolError::token_expired`. Such a session can then be
  524. /// revived by calling refresh() with a newly acquired access token.
  525. ///
  526. /// Due to protocol techicalities, a race condition exists that can cause a
  527. /// session to become, and remain disconnected after a new access token has
  528. /// been passed to refresh(). The application can work around this race
  529. /// condition by detecting the `ProtocolError::token_expired` error, and
  530. /// always initiate a token renewal in this case.
  531. ///
  532. /// It is an error to call this function before calling `Client::bind()`.
  533. ///
  534. /// Note: This function is thread-safe.
  535. ///
  536. /// \param signed_user_token A cryptographically signed token describing the
  537. /// identity and access rights of the current user. See ProtocolEnvelope.
  538. void refresh(const std::string& signed_user_token);
  539. /// \brief Inform the synchronization agent about changes of local origin.
  540. ///
  541. /// This function must be called by the application after a transaction
  542. /// performed on its behalf, that is, after a transaction that is not
  543. /// performed to integrate a changeset that was downloaded from the server.
  544. ///
  545. /// It is an error to call this function before bind() has been called, and
  546. /// has returned.
  547. ///
  548. /// Note: This function is fully thread-safe. That is, it may be called by
  549. /// any thread, and by multiple threads concurrently.
  550. void nonsync_transact_notify(version_type new_version);
  551. /// @{ \brief Wait for upload, download, or upload+download completion.
  552. ///
  553. /// async_wait_for_upload_completion() initiates an asynchronous wait for
  554. /// upload to complete, async_wait_for_download_completion() initiates an
  555. /// asynchronous wait for download to complete, and
  556. /// async_wait_for_sync_completion() initiates an asynchronous wait for
  557. /// upload and download to complete.
  558. ///
  559. /// Upload is considered complete when all non-empty changesets of local
  560. /// origin have been uploaded to the server, and the server has acknowledged
  561. /// reception of them. Changesets of local origin introduced after the
  562. /// initiation of the session (after bind() is called) will generally not be
  563. /// considered for upload unless they are announced to this client through
  564. /// nonsync_transact_notify() prior to the initiation of the wait operation,
  565. /// i.e., prior to the invocation of async_wait_for_upload_completion() or
  566. /// async_wait_for_sync_completion(). Unannounced changesets may get picked
  567. /// up, but there is no guarantee that they will be, however, if a certain
  568. /// changeset is announced, then all previous changesets are implicitly
  569. /// announced. Also all preexisting changesets are implicitly announced
  570. /// when the session is initiated.
  571. ///
  572. /// Download is considered complete when all non-empty changesets of remote
  573. /// origin have been downloaded from the server, and integrated into the
  574. /// local Realm state. To know what is currently outstanding on the server,
  575. /// the client always sends a special "marker" message to the server, and
  576. /// waits until it has downloaded all outstanding changesets that were
  577. /// present on the server at the time when the server received that marker
  578. /// message. Each call to async_wait_for_download_completion() and
  579. /// async_wait_for_sync_completion() therefore requires a full client <->
  580. /// server round-trip.
  581. ///
  582. /// If a new wait operation is initiated while another wait operation is in
  583. /// progress by another thread, the waiting period of first operation may,
  584. /// or may not get extended. The application must not assume either. The
  585. /// application may assume, however, that async_wait_for_upload_completion()
  586. /// will not affect the waiting period of
  587. /// async_wait_for_download_completion(), and vice versa.
  588. ///
  589. /// It is an error to call these functions before bind() has been called,
  590. /// and has returned.
  591. ///
  592. /// The specified completion handlers will always be executed by the thread
  593. /// that executes the event loop (the thread that calls Client::run()). If
  594. /// the handler throws an exception, that exception will "travel" out
  595. /// through Client::run().
  596. ///
  597. /// If incomplete wait operations exist when the session is terminated,
  598. /// those wait operations will be canceled. Session termination is an event
  599. /// that happens in the context of the client's event loop thread shortly
  600. /// after the destruction of the session object. The std::error_code
  601. /// argument passed to the completion handler of a canceled wait operation
  602. /// will be `util::error::operation_aborted`. For uncanceled wait operations
  603. /// it will be `std::error_code{}`. Note that as long as the client's event
  604. /// loop thread is running, all completion handlers will be called
  605. /// regardless of whether the operations get canceled or not.
  606. ///
  607. /// CAUTION: The specified completion handlers may get called before the
  608. /// call to the waiting function returns, and it may get called (or continue
  609. /// to execute) after the session object is destroyed. Please see "Callback
  610. /// semantics" section under Session for more on this.
  611. ///
  612. /// Note: These functions are fully thread-safe. That is, they may be called
  613. /// by any thread, and by multiple threads concurrently.
  614. void async_wait_for_sync_completion(WaitOperCompletionHandler);
  615. void async_wait_for_upload_completion(WaitOperCompletionHandler);
  616. void async_wait_for_download_completion(WaitOperCompletionHandler);
  617. /// @}
  618. /// @{ \brief Synchronous wait for upload or download completion.
  619. ///
  620. /// These functions are synchronous equivalents of
  621. /// async_wait_for_upload_completion() and
  622. /// async_wait_for_download_completion() respectively. This means that they
  623. /// block the caller until the completion condition is satisfied, or the
  624. /// client's event loop thread exits from Client::run(), whichever happens
  625. /// first.
  626. ///
  627. /// It is an error to call these functions before bind() has been called,
  628. /// and has returned.
  629. ///
  630. /// CAUTION: If Client::run() returns while a wait operation is in progress,
  631. /// these waiting functions return immediately, even if the completion
  632. /// condition is not yet satisfied. The completion condition is guaranteed
  633. /// to be satisfied only when these functions return true.
  634. ///
  635. /// \return True only if the completion condition was satisfied. False if
  636. /// the client's event loop thread exited from Client::run() in which case
  637. /// the completion condition may, or may not have been satisfied.
  638. ///
  639. /// Note: These functions are fully thread-safe. That is, they may be called
  640. /// by any thread, and by multiple threads concurrently.
  641. bool wait_for_upload_complete_or_client_stopped();
  642. bool wait_for_download_complete_or_client_stopped();
  643. /// @}
  644. /// \brief Cancel the current or next reconnect delay for the server
  645. /// associated with this session.
  646. ///
  647. /// When the network connection is severed, or an attempt to establish
  648. /// connection fails, a certain delay will take effect before the client
  649. /// will attempt to reestablish the connection. This delay will generally
  650. /// grow with the number of unsuccessful reconnect attempts, and can grow to
  651. /// over a minute. In some cases however, the application will know when it
  652. /// is a good time to stop waiting and retry immediately. One example is
  653. /// when a device has been offline for a while, and the operating system
  654. /// then tells the application that network connectivity has been restored.
  655. ///
  656. /// Clearly, this function should not be called too often and over extended
  657. /// periods of time, as that would effectively disable the built-in "server
  658. /// hammering" protection.
  659. ///
  660. /// It is an error to call this function before bind() has been called, and
  661. /// has returned.
  662. ///
  663. /// This function is fully thread-safe. That is, it may be called by any
  664. /// thread, and by multiple threads concurrently.
  665. void cancel_reconnect_delay();
  666. void on_new_flx_sync_subscription(int64_t new_version);
  667. util::Future<std::string> send_test_command(std::string command_body);
  668. private:
  669. SessionWrapper* m_impl = nullptr;
  670. void abandon() noexcept;
  671. void async_wait_for(bool upload_completion, bool download_completion, WaitOperCompletionHandler);
  672. };
  673. std::ostream& operator<<(std::ostream& os, SyncConfig::ProxyConfig::Type);
  674. // Implementation
  675. class BadServerUrl : public Exception {
  676. public:
  677. BadServerUrl(std::string_view url)
  678. : Exception(ErrorCodes::BadServerUrl, util::format("Unable to parse server URL '%1'", url))
  679. {
  680. }
  681. };
  682. inline Session::Session(Session&& sess) noexcept
  683. : m_impl{sess.m_impl}
  684. {
  685. sess.m_impl = nullptr;
  686. }
  687. inline Session::~Session() noexcept
  688. {
  689. if (m_impl)
  690. abandon();
  691. }
  692. inline Session& Session::operator=(Session&& sess) noexcept
  693. {
  694. if (m_impl)
  695. abandon();
  696. m_impl = sess.m_impl;
  697. sess.m_impl = nullptr;
  698. return *this;
  699. }
  700. inline void Session::detach() noexcept
  701. {
  702. if (m_impl)
  703. abandon();
  704. m_impl = nullptr;
  705. }
  706. inline void Session::set_error_handler(util::UniqueFunction<ErrorHandler> handler)
  707. {
  708. auto handler_2 = [handler = std::move(handler)](ConnectionState state,
  709. const util::Optional<SessionErrorInfo>& error_info) {
  710. if (state != ConnectionState::disconnected)
  711. return;
  712. REALM_ASSERT(error_info);
  713. handler(*error_info); // Throws
  714. };
  715. set_connection_state_change_listener(std::move(handler_2)); // Throws
  716. }
  717. inline void Session::async_wait_for_sync_completion(WaitOperCompletionHandler handler)
  718. {
  719. bool upload_completion = true, download_completion = true;
  720. async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
  721. }
  722. inline void Session::async_wait_for_upload_completion(WaitOperCompletionHandler handler)
  723. {
  724. bool upload_completion = true, download_completion = false;
  725. async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
  726. }
  727. inline void Session::async_wait_for_download_completion(WaitOperCompletionHandler handler)
  728. {
  729. bool upload_completion = false, download_completion = true;
  730. async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
  731. }
  732. } // namespace realm::sync
  733. #endif // REALM_SYNC_CLIENT_HPP