client.hpp 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161
  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/logger.hpp>
  12. #include <realm/util/network.hpp>
  13. #include <realm/impl/cont_transact_hist.hpp>
  14. #include <realm/sync/protocol.hpp>
  15. #include <realm/sync/history.hpp>
  16. namespace realm {
  17. namespace sync {
  18. class Client {
  19. public:
  20. enum class Error;
  21. using port_type = util::network::Endpoint::port_type;
  22. using RoundtripTimeHandler = void(milliseconds_type roundtrip_time);
  23. static constexpr milliseconds_type default_connect_timeout = 120000; // 2 minutes
  24. static constexpr milliseconds_type default_connection_linger_time = 30000; // 30 seconds
  25. static constexpr milliseconds_type default_ping_keepalive_period = 60000; // 1 minute
  26. static constexpr milliseconds_type default_pong_keepalive_timeout = 120000; // 2 minutes
  27. static constexpr milliseconds_type default_fast_reconnect_limit = 60000; // 1 minute
  28. struct Config {
  29. Config() {}
  30. /// An optional custom platform description to be sent to server as part
  31. /// of a user agent description (HTTP `User-Agent` header).
  32. ///
  33. /// If left empty, the platform description will be whatever is returned
  34. /// by util::get_platform_info().
  35. std::string user_agent_platform_info;
  36. /// Optional information about the application to be added to the user
  37. /// agent description as sent to the server. The intention is that the
  38. /// application describes itself using the following (rough) syntax:
  39. ///
  40. /// <application info> ::= (<space> <layer>)*
  41. /// <layer> ::= <name> "/" <version> [<space> <details>]
  42. /// <name> ::= (<alnum>)+
  43. /// <version> ::= <digit> (<alnum> | "." | "-" | "_")*
  44. /// <details> ::= <parentherized>
  45. /// <parentherized> ::= "(" (<nonpar> | <parentherized>)* ")"
  46. ///
  47. /// Where `<space>` is a single space character, `<digit>` is a decimal
  48. /// digit, `<alnum>` is any alphanumeric character, and `<nonpar>` is
  49. /// any character other than `(` and `)`.
  50. ///
  51. /// When multiple levels are present, the innermost layer (the one that
  52. /// is closest to this API) should appear first.
  53. ///
  54. /// Example:
  55. ///
  56. /// RealmJS/2.13.0 RealmStudio/2.9.0
  57. ///
  58. /// Note: The user agent description is not intended for machine
  59. /// interpretation, but should still follow the specified syntax such
  60. /// that it remains easily interpretable by human beings.
  61. std::string user_agent_application_info;
  62. /// An optional logger to be used by the client. If no logger is
  63. /// specified, the client will use an instance of util::StderrLogger
  64. /// with the log level threshold set to util::Logger::Level::info. The
  65. /// client does not require a thread-safe logger, and it guarantees that
  66. /// all logging happens either on behalf of the constructor or on behalf
  67. /// of the invocation of run().
  68. util::Logger* logger = nullptr;
  69. /// Use ports 80 and 443 by default instead of 7800 and 7801
  70. /// respectively. Ideally, these default ports should have been made
  71. /// available via a different URI scheme instead (http/https or ws/wss).
  72. bool enable_default_port_hack = true;
  73. /// For testing purposes only.
  74. ReconnectMode reconnect_mode = ReconnectMode::normal;
  75. /// Create a separate connection for each session. For testing purposes
  76. /// only.
  77. ///
  78. /// FIXME: This setting needs to be true for now, due to limitations in
  79. /// the load balancer.
  80. bool one_connection_per_session = true;
  81. /// Do not access the local file system. Sessions will act as if
  82. /// initiated on behalf of an empty (or nonexisting) local Realm
  83. /// file. Received DOWNLOAD messages will be accepted, but otherwise
  84. /// ignored. No UPLOAD messages will be generated. For testing purposes
  85. /// only.
  86. ///
  87. /// Many operations, such as serialized transactions, are not suppored
  88. /// in this mode.
  89. bool dry_run = false;
  90. /// The maximum number of milliseconds to allow for a connection to
  91. /// become fully established. This includes the time to resolve the
  92. /// network address, the TCP connect operation, the SSL handshake, and
  93. /// the WebSocket handshake.
  94. milliseconds_type connect_timeout = default_connect_timeout;
  95. /// The number of milliseconds to keep a connection open after all
  96. /// sessions have been abandoned (or suspended by errors).
  97. ///
  98. /// The purpose of this linger time is to avoid close/reopen cycles
  99. /// during short periods of time where there are no sessions interested
  100. /// in using the connection.
  101. ///
  102. /// If the connection gets closed due to an error before the linger time
  103. /// expires, the connection will be kept closed until there are sessions
  104. /// willing to use it again.
  105. milliseconds_type connection_linger_time = default_connection_linger_time;
  106. /// The client will send PING messages periodically to allow the server
  107. /// to detect dead connections (heartbeat). This parameter specifies the
  108. /// time, in milliseconds, between these PING messages. When scheduling
  109. /// the next PING message, the client will deduct a small random amount
  110. /// from the specified value to help spread the load on the server from
  111. /// many clients.
  112. milliseconds_type ping_keepalive_period = default_ping_keepalive_period;
  113. /// Whenever the server receives a PING message, it is supposed to
  114. /// respond with a PONG messsage to allow the client to detect dead
  115. /// connections (heartbeat). This parameter specifies the time, in
  116. /// milliseconds, that the client will wait for the PONG response
  117. /// message before it assumes that the connection is dead, and
  118. /// terminates it.
  119. milliseconds_type pong_keepalive_timeout = default_pong_keepalive_timeout;
  120. /// The maximum amount of time, in milliseconds, since the loss of a
  121. /// prior connection, for a new connection to be considered a *fast
  122. /// reconnect*.
  123. ///
  124. /// In general, when a client establishes a connection to the server,
  125. /// the uploading process remains suspended until the initial
  126. /// downloading process completes (as if by invocation of
  127. /// Session::async_wait_for_download_completion()). However, to avoid
  128. /// unnecessary latency in change propagation during ongoing
  129. /// application-level activity, if the new connection is established
  130. /// less than a certain amount of time (`fast_reconnect_limit`) since
  131. /// the client was previously connected to the server, then the
  132. /// uploading process will be activated immediately.
  133. ///
  134. /// For now, the purpose of the general delaying of the activation of
  135. /// the uploading process, is to increase the chance of multiple initial
  136. /// transactions on the client-side, to be uploaded to, and processed by
  137. /// the server as a single unit. In the longer run, the intention is
  138. /// that the client should upload transformed (from reciprocal history),
  139. /// rather than original changesets when applicable to reduce the need
  140. /// for changeset to be transformed on both sides. The delaying of the
  141. /// upload process will increase the number of cases where this is
  142. /// possible.
  143. ///
  144. /// FIXME: Currently, the time between connections is not tracked across
  145. /// sessions, so if the application closes its session, and opens a new
  146. /// one immediately afterwards, the activation of the upload process
  147. /// will be delayed unconditionally.
  148. milliseconds_type fast_reconnect_limit = default_fast_reconnect_limit;
  149. /// Set to true to completely disable delaying of the upload process. In
  150. /// this mode, the upload process will be activated immediately, and the
  151. /// value of `fast_reconnect_limit` is ignored.
  152. ///
  153. /// For testing purposes only.
  154. bool disable_upload_activation_delay = false;
  155. /// If `disable_upload_compaction` is true, every changeset will be
  156. /// compacted before it is uploaded to the server. Compaction will
  157. /// reduce the size of a changeset if the same field is set multiple
  158. /// times or if newly created objects are deleted within the same
  159. /// transaction. Log compaction increeses CPU usage and memory
  160. /// consumption.
  161. bool disable_upload_compaction = false;
  162. /// Set the `TCP_NODELAY` option on all TCP/IP sockets. This disables
  163. /// the Nagle algorithm. Disabling it, can in some cases be used to
  164. /// decrease latencies, but possibly at the expense of scalability. Be
  165. /// sure to research the subject before you enable this option.
  166. bool tcp_no_delay = false;
  167. /// The specified function will be called whenever a PONG message is
  168. /// received on any connection. The round-trip time in milliseconds will
  169. /// be pased to the function. The specified function will always be
  170. /// called by the client's event loop thread, i.e., the thread that
  171. /// calls `Client::run()`. This feature is mainly for testing purposes.
  172. std::function<RoundtripTimeHandler> roundtrip_time_handler;
  173. /// Disable sync to disk (fsync(), msync()) for all realm files managed
  174. /// by this client.
  175. ///
  176. /// Testing/debugging feature. Should never be enabled in production.
  177. bool disable_sync_to_disk = false;
  178. };
  179. /// \throw util::EventLoop::Implementation::NotAvailable if no event loop
  180. /// implementation was specified, and
  181. /// util::EventLoop::Implementation::get_default() throws it.
  182. Client(Config = {});
  183. Client(Client&&) noexcept;
  184. ~Client() noexcept;
  185. /// Run the internal event-loop of the client. At most one thread may
  186. /// execute run() at any given time. The call will not return until somebody
  187. /// calls stop().
  188. void run();
  189. /// See run().
  190. ///
  191. /// Thread-safe.
  192. void stop() noexcept;
  193. /// \brief Cancel current or next reconnect delay for all servers.
  194. ///
  195. /// This corresponds to calling Session::cancel_reconnect_delay() on all
  196. /// bound sessions, but will also cancel reconnect delays applying to
  197. /// servers for which there are currently no bound sessions.
  198. ///
  199. /// Thread-safe.
  200. void cancel_reconnect_delay();
  201. /// \brief Wait for session termination to complete.
  202. ///
  203. /// Wait for termination of all sessions whose termination was initiated
  204. /// prior this call (the completion condition), or until the client's event
  205. /// loop thread exits from Client::run(), whichever happens
  206. /// first. Termination of a session can be initiated implicitly (e.g., via
  207. /// destruction of the session object), or explicitly by Session::detach().
  208. ///
  209. /// Note: After session termination (when this function returns true) no
  210. /// session specific callback function can be called or continue to execute,
  211. /// and the client is guaranteed to no longer have a Realm file open on
  212. /// behalf of the terminated session.
  213. ///
  214. /// CAUTION: If run() returns while a wait operation is in progress, this
  215. /// waiting function will return immediately, even if the completion
  216. /// condition is not yet satisfied. The completion condition is guaranteed
  217. /// to be satisfied only when these functions return true. If it returns
  218. /// false, session specific callback functions may still be executing or get
  219. /// called, and the associated Realm files may still not have been closed.
  220. ///
  221. /// If a new wait operation is initiated while another wait operation is in
  222. /// progress by another thread, the waiting period of fist operation may, or
  223. /// may not get extended. The application must not assume either.
  224. ///
  225. /// Note: Session termination does not imply that the client has received an
  226. /// UNBOUND message from the server (see the protocol specification). This
  227. /// may happen later.
  228. ///
  229. /// \return True only if the completion condition was satisfied. False if
  230. /// the client's event loop thread exited from Client::run() in which case
  231. /// the completion condition may, or may not have been satisfied.
  232. ///
  233. /// Note: These functions are fully thread-safe. That is, they may be called
  234. /// by any thread, and by multiple threads concurrently.
  235. bool wait_for_session_terminations_or_client_stopped();
  236. /// Returns false if the specified URL is invalid.
  237. bool decompose_server_url(const std::string& url, ProtocolEnvelope& protocol, std::string& address,
  238. port_type& port, std::string& path) const;
  239. private:
  240. class Impl;
  241. std::unique_ptr<Impl> m_impl;
  242. friend class Session;
  243. };
  244. class BadServerUrl; // Exception
  245. /// \brief Client-side representation of a Realm file synchronization session.
  246. ///
  247. /// A synchronization session deals with precisely one local Realm file. To
  248. /// synchronize multiple local Realm files, you need multiple sessions.
  249. ///
  250. /// A session object is always associated with a particular client object (\ref
  251. /// Client). The application must ensure that the destruction of the associated
  252. /// client object never happens before the destruction of the session
  253. /// object. The consequences of a violation are unspecified.
  254. ///
  255. /// A session object is always associated with a particular local Realm file,
  256. /// however, a session object does not represent a session until it is bound to
  257. /// a server side Realm, i.e., until bind() is called. From the point of view of
  258. /// the thread that calls bind(), the session starts precisely when the
  259. /// execution of bind() starts, i.e., before bind() returns.
  260. ///
  261. /// At most one session is allowed to exist for a particular local Realm file
  262. /// (file system inode) at any point in time. Multiple session objects may
  263. /// coexists for a single file, as long as bind() has been called on at most one
  264. /// of them. Additionally, two bound session objects for the same file are
  265. /// allowed to exist at different times, if they have no overlap in time (in
  266. /// their bound state), as long as they are associated with the same client
  267. /// object, or with two different client objects that do not overlap in
  268. /// time. This means, in particular, that it is an error to create two bound
  269. /// session objects for the same local Realm file, it they are associated with
  270. /// two different client objects that overlap in time, even if the session
  271. /// objects do not overlap in time (in their bound state). It is the
  272. /// responsibility of the application to ensure that these rules are adhered
  273. /// to. The consequences of a violation are unspecified.
  274. ///
  275. /// Thread-safety: It is safe for multiple threads to construct, use (with some
  276. /// exceptions), and destroy session objects concurrently, regardless of whether
  277. /// those session objects are associated with the same, or with different Client
  278. /// objects. Please note that some of the public member functions are fully
  279. /// thread-safe, while others are not.
  280. ///
  281. /// Callback semantics: All session specific callback functions will be executed
  282. /// by the event loop thread, i.e., the thread that calls Client::run(). No
  283. /// callback function will be called before Session::bind() is called. Callback
  284. /// functions that are specified prior to calling bind() (e.g., any passed to
  285. /// set_progress_handler()) may start to execute before bind() returns, as long
  286. /// as some thread is executing Client::run(). Likewise, completion handlers,
  287. /// such as those passed to async_wait_for_sync_completion() may start to
  288. /// execute before the submitting function returns. All session specific
  289. /// callback functions (including completion handlers) are guaranteed to no
  290. /// longer be executing when session termination completes, and they are
  291. /// guaranteed to not be called after session termination completes. Termination
  292. /// is an event that completes asynchronously with respect to the application,
  293. /// but is initiated by calling detach(), or implicitly by destroying a session
  294. /// object. After having initiated one or more session terminations, the
  295. /// application can wait for those terminations to complete by calling
  296. /// Client::wait_for_session_terminations_or_client_stopped(). Since callback
  297. /// functions are always executed by the event loop thread, they are also
  298. /// guaranteed to not be executing after Client::run() has returned.
  299. class Session {
  300. public:
  301. using port_type = util::network::Endpoint::port_type;
  302. using SyncTransactCallback = void(VersionID old_version, VersionID new_version);
  303. using ProgressHandler = void(std::uint_fast64_t downloaded_bytes, std::uint_fast64_t downloadable_bytes,
  304. std::uint_fast64_t uploaded_bytes, std::uint_fast64_t uploadable_bytes,
  305. std::uint_fast64_t progress_version, std::uint_fast64_t snapshot_version);
  306. using WaitOperCompletionHandler = std::function<void(std::error_code)>;
  307. using SSLVerifyCallback = bool(const std::string& server_address, port_type server_port, const char* pem_data,
  308. size_t pem_size, int preverify_ok, int depth);
  309. struct Config {
  310. Config() {}
  311. /// server_address is the fully qualified host name, or IP address of
  312. /// the server.
  313. std::string server_address = "localhost";
  314. /// server_port is the port at which the server listens. If server_port
  315. /// is zero, the default port for the specified protocol is used. See
  316. /// ProtocolEnvelope for information on default ports.
  317. port_type server_port = 0;
  318. /// realm_identifier is the virtual path by which the server identifies the
  319. /// Realm.
  320. /// When connecting to the mock C++ server, this path must always be an
  321. /// absolute path, and must therefore always contain a leading slash (`/`).
  322. /// Furthermore, each segment of the virtual path must consist of one or
  323. /// more characters that are either alpha-numeric or in (`_`, `-`, `.`),
  324. /// and each segment is not allowed to equal `.` or `..`, and must not end
  325. /// with `.realm`, `.realm.lock`, or `.realm.management`. These rules are
  326. /// necessary because the C++ server currently reserves the right to use the
  327. /// specified path as part of the file system path of a Realm file.
  328. /// On the MongoDB Realm-based Sync server, virtual paths are not coupled
  329. /// to file system paths, and thus, these restrictions do not apply.
  330. std::string realm_identifier = "";
  331. /// The protocol used for communicating with the server. See
  332. /// ProtocolEnvelope.
  333. ProtocolEnvelope protocol_envelope = ProtocolEnvelope::realm;
  334. /// service_identifier is a prefix that is prepended to the realm_identifier
  335. /// in the HTTP GET request that initiates a sync connection. The value
  336. /// specified here must match with the server's expectation. Changing
  337. /// the value of service_identifier should be matched with a corresponding
  338. /// change in the C++ mock server.
  339. std::string service_identifier = "";
  340. /// authorization_header_name is the name of the HTTP header containing
  341. /// the Realm access token. The value of the HTTP header is "Bearer <token>".
  342. /// authorization_header_name does not participate in session
  343. /// multiplexing partitioning.
  344. std::string authorization_header_name = "Authorization";
  345. /// custom_http_headers is a map of custom HTTP headers. The keys of the map
  346. /// are HTTP header names, and the values are the corresponding HTTP
  347. /// header values.
  348. /// If "Authorization" is used as a custom header name,
  349. /// authorization_header_name must be set to anther value.
  350. std::map<std::string, std::string> custom_http_headers;
  351. /// Sessions can be multiplexed over the same TCP/SSL connection.
  352. /// Sessions might share connection if they have identical server_address,
  353. /// server_port, and protocol. multiplex_ident is a parameter that allows
  354. /// finer control over session multiplexing. If two sessions have distinct
  355. /// multiplex_ident, they will never share connection. The typical use of
  356. /// multilex_ident is to give sessions with incompatible SSL requirements
  357. /// distinct multiplex_idents.
  358. /// multiplex_ident can be any string and the value has no meaning except
  359. /// for partitioning the sessions.
  360. std::string multiplex_ident;
  361. /// Controls whether the server certificate is verified for SSL
  362. /// connections. It should generally be true in production.
  363. bool verify_servers_ssl_certificate = true;
  364. /// ssl_trust_certificate_path is the path of a trust/anchor
  365. /// certificate used by the client to verify the server certificate.
  366. /// ssl_trust_certificate_path is only used if the protocol is ssl and
  367. /// verify_servers_ssl_certificate is true.
  368. ///
  369. /// A server certificate is verified by first checking that the
  370. /// certificate has a valid signature chain back to a trust/anchor
  371. /// certificate, and secondly checking that the server_address matches
  372. /// a host name contained in the certificate. The host name of the
  373. /// certificate is stored in either Common Name or the Alternative
  374. /// Subject Name (DNS section).
  375. ///
  376. /// If ssl_trust_certificate_path is None (default), ssl_verify_callback
  377. /// (see below) is used if set, and the default device trust/anchor
  378. /// store is used otherwise.
  379. util::Optional<std::string> ssl_trust_certificate_path;
  380. /// If Client::Config::ssl_verify_callback is set, that function is called
  381. /// to verify the certificate, unless verify_servers_ssl_certificate is
  382. /// false.
  383. /// ssl_verify_callback is used to implement custom SSL certificate
  384. /// verification. it is only used if the protocol is SSL,
  385. /// verify_servers_ssl_certificate is true and ssl_trust_certificate_path
  386. /// is None.
  387. ///
  388. /// The signature of ssl_verify_callback is
  389. ///
  390. /// bool(const std::string& server_address,
  391. /// port_type server_port,
  392. /// const char* pem_data,
  393. /// size_t pem_size,
  394. /// int preverify_ok,
  395. /// int depth);
  396. ///
  397. /// server address and server_port is the address and port of the server
  398. /// that a SSL connection is being established to. They are identical to
  399. /// the server_address and server_port set in this config file and are
  400. /// passed for convenience.
  401. /// pem_data is the certificate of length pem_size in
  402. /// the PEM format. preverify_ok is OpenSSL's preverification of the
  403. /// certificate. preverify_ok is either 0, or 1. If preverify_ok is 1,
  404. /// OpenSSL has accepted the certificate and it will generally be safe
  405. /// to trust that certificate. depth represents the position of the
  406. /// certificate in the certificate chain sent by the server. depth = 0
  407. /// represents the actual server certificate that should contain the
  408. /// host name(server address) of the server. The highest depth is the
  409. /// root certificate.
  410. /// The callback function will receive the certificates starting from
  411. /// the root certificate and moving down the chain until it reaches the
  412. /// server's own certificate with a host name. The depth of the last
  413. /// certificate is 0. The depth of the first certificate is chain
  414. /// length - 1.
  415. ///
  416. /// The return value of the callback function decides whether the
  417. /// client accepts the certificate. If the return value is false, the
  418. /// processing of the certificate chain is interrupted and the SSL
  419. /// connection is rejected. If the return value is true, the verification
  420. /// process continues. If the callback function returns true for all
  421. /// presented certificates including the depth == 0 certificate, the
  422. /// SSL connection is accepted.
  423. ///
  424. /// A recommended way of using the callback function is to return true
  425. /// if preverify_ok = 1 and depth > 0,
  426. /// always check the host name if depth = 0,
  427. /// and use an independent verification step if preverify_ok = 0.
  428. ///
  429. /// Another possible way of using the callback is to collect all the
  430. /// certificates until depth = 0, and present the entire chain for
  431. /// independent verification.
  432. std::function<SSLVerifyCallback> ssl_verify_callback;
  433. /// signed_user_token is a cryptographically signed token describing the
  434. /// identity and access rights of the current user.
  435. std::string signed_user_token;
  436. /// ClientReset is used for both async open and client reset. If
  437. /// client_reset is not util::none, the sync client will perform
  438. /// async open for this session if the local Realm does not exist, and
  439. /// client reset if the local Realm exists. If client_reset is
  440. /// util::none, an ordinary sync session will take place.
  441. ///
  442. /// A session will perform async open by downloading a state Realm, and
  443. /// some metadata, from the server, patching up the metadata part of
  444. /// the Realm and finally move the downloaded Realm into the path of
  445. /// the local Realm. After completion of async open, the application
  446. /// can open and use the Realm.
  447. ///
  448. /// A session will perform client reset by downloading a state Realm, and
  449. /// some metadata, from the server. After download, the state Realm will
  450. /// be integrated into the local Realm in a write transaction. The
  451. /// application is free to use the local realm during the entire client
  452. /// reset. Like a DOWNLOAD message, the application will not be able
  453. /// to perform a write transaction at the same time as the sync client
  454. /// performs its own write transaction. Client reset is not more
  455. /// disturbing for the application than any DOWNLOAD message. The
  456. /// application can listen to change notifications from the client
  457. /// reset exactly as in a DOWNLOAD message.
  458. ///
  459. /// Async open and client reset require a private directory for
  460. /// metadata. This directory must be specified in the option
  461. /// 'metadata_dir'. The metadata_dir must not be touched during async
  462. /// open or client reset. The metadata_dir can safely be removed at
  463. /// times where async open or client reset do not take place. The sync
  464. /// client attempts to clean up metadata_dir. The metadata_dir can be
  465. /// reused across app restarts to resume an interrupted download. It is
  466. /// recommended to leave the metadata_dir unchanged except when it is
  467. /// known that async open or client reset is done.
  468. ///
  469. /// The recommended usage of async open is to use it for the initial
  470. /// bootstrap if Realm usage is not needed until after the server state
  471. /// has been downloaded.
  472. ///
  473. /// The recommended usage of client reset is after a previous session
  474. /// encountered an error that implies the need for a client reset. It
  475. /// is not recommended to persist the need for a client reset. The
  476. /// application should just attempt to synchronize in the usual fashion
  477. /// and only after hitting an error, start a new session with a client
  478. /// reset. In other words, if the application crashes during a client reset,
  479. /// the application should attempt to perform ordinary synchronization
  480. /// after restart and switch to client reset if needed.
  481. ///
  482. /// Error codes that imply the need for a client reset are the session
  483. /// level error codes:
  484. ///
  485. /// bad_client_file_ident = 208, // Bad client file identifier (IDENT)
  486. /// bad_server_version = 209, // Bad server version (IDENT, UPLOAD)
  487. /// bad_client_version = 210, // Bad client version (IDENT, UPLOAD)
  488. /// diverging_histories = 211, // Diverging histories (IDENT)
  489. ///
  490. /// However, other errors such as bad changeset (UPLOAD) could also be resolved
  491. /// with a client reset. Client reset can even be used without any prior error
  492. /// if so desired.
  493. ///
  494. /// After completion of async open and client reset, the sync client
  495. /// will continue synchronizing with the server in the usual fashion.
  496. ///
  497. /// The progress of async open and client reset can be tracked with the
  498. /// standard progress handler.
  499. ///
  500. /// Async open and client reset are done when the progress handler
  501. /// arguments satisfy "progress_version > 0". However, if the
  502. /// application wants to ensure that it has all data present on the
  503. /// server, it should wait for download completion using either
  504. /// void async_wait_for_download_completion(WaitOperCompletionHandler)
  505. /// or
  506. /// bool wait_for_download_complete_or_client_stopped().
  507. struct ClientReset {
  508. std::string metadata_dir;
  509. };
  510. util::Optional<ClientReset> client_reset_config;
  511. util::Optional<SyncConfig::ProxyConfig> proxy_config;
  512. /// Set to true to disable the upload process for this session. This
  513. /// includes the sending of empty UPLOAD messages.
  514. ///
  515. /// This feature exists exclusively for testing purposes at this time.
  516. bool disable_upload = false;
  517. /// Set to true to disable sending of empty UPLOAD messages for this
  518. /// session.
  519. ///
  520. /// This feature exists exclusively for testing purposes at this time.
  521. bool disable_empty_upload = false;
  522. /// Set to true to cause the integration of the first received changeset
  523. /// (in a DOWNLOAD message) to fail.
  524. ///
  525. /// This feature exists exclusively for testing purposes at this time.
  526. bool simulate_integration_error = false;
  527. };
  528. /// \brief Start a new session for the specified client-side Realm.
  529. ///
  530. /// Note that the session is not fully activated until you call bind().
  531. /// Also note that if you call set_sync_transact_callback(), it must be
  532. /// done before calling bind().
  533. Session(Client&, std::shared_ptr<DB>, Config = {});
  534. /// This leaves the right-hand side session object detached. See "Thread
  535. /// safety" section under detach().
  536. Session(Session&&) noexcept;
  537. /// Create a detached session object (see detach()).
  538. Session() noexcept;
  539. /// Implies detachment. See "Thread safety" section under detach().
  540. ~Session() noexcept;
  541. /// Detach the object on the left-hand side, then "steal" the session from
  542. /// the object on the right-hand side, if there is one. This leaves the
  543. /// object on the right-hand side detached. See "Thread safety" section
  544. /// under detach().
  545. Session& operator=(Session&&) noexcept;
  546. /// Detach this session object from the client object (Client). If the
  547. /// session object is already detached, this function has no effect
  548. /// (idempotency).
  549. ///
  550. /// Detachment initiates session termination, which is an event that takes
  551. /// place shortly thereafter in the context of the client's event loop
  552. /// thread.
  553. ///
  554. /// A detached session object may be destroyed, move-assigned to, and moved
  555. /// from. Apart from that, it is an error to call any function other than
  556. /// detach() on a detached session object.
  557. ///
  558. /// Thread safety: Detachment is not a thread-safe operation. This means
  559. /// that detach() may not be executed by two threads concurrently, and may
  560. /// not execute concurrently with object destruction. Additionally,
  561. /// detachment must not execute concurrently with a moving operation
  562. /// involving the session object on the left or right-hand side. See move
  563. /// constructor and assignment operator.
  564. void detach() noexcept;
  565. /// \brief Set a function to be called when the local Realm has changed due
  566. /// to integration of a downloaded changeset.
  567. ///
  568. /// Specify the callback function that will be called when one or more
  569. /// transactions are performed to integrate downloaded changesets into the
  570. /// client-side Realm, that is associated with this session.
  571. ///
  572. /// The callback function will always be called by the thread that executes
  573. /// the event loop (Client::run()), but not until bind() is called. If the
  574. /// callback function throws an exception, that exception will "travel" out
  575. /// through Client::run().
  576. ///
  577. /// Note: Any call to this function must have returned before bind() is
  578. /// called. If this function is called multiple times, each call overrides
  579. /// the previous setting.
  580. ///
  581. /// Note: This function is **not thread-safe**. That is, it is an error if
  582. /// it is called while another thread is executing any member function on
  583. /// the same Session object.
  584. ///
  585. /// CAUTION: The specified callback function may get called before the call
  586. /// to bind() returns, and it may get called (or continue to execute) after
  587. /// the session object is destroyed. Please see "Callback semantics" section
  588. /// under Session for more on this.
  589. void set_sync_transact_callback(std::function<SyncTransactCallback>);
  590. /// \brief Set a handler to monitor the state of download and upload
  591. /// progress.
  592. ///
  593. /// The handler must have signature
  594. ///
  595. /// void(uint_fast64_t downloaded_bytes, uint_fast64_t downloadable_bytes,
  596. /// uint_fast64_t uploaded_bytes, uint_fast64_t uploadable_bytes,
  597. /// uint_fast64_t progress_version);
  598. ///
  599. /// downloaded_bytes is the size in bytes of all downloaded changesets.
  600. /// downloadable_bytes is equal to downloaded_bytes plus an estimate of
  601. /// the size of the remaining server history.
  602. ///
  603. /// uploaded_bytes is the size in bytes of all locally produced changesets
  604. /// that have been received and acknowledged by the server.
  605. /// uploadable_bytes is the size in bytes of all locally produced changesets.
  606. ///
  607. /// Due to the nature of the merge rules, it is possible that the size of an
  608. /// uploaded changeset uploaded from one client is not equal to the size of
  609. /// the changesets that other clients will download.
  610. ///
  611. /// Typical uses of this function:
  612. ///
  613. /// Upload completion can be checked by
  614. ///
  615. /// bool upload_complete = (uploaded_bytes == uploadable_bytes);
  616. ///
  617. /// Download completion could be checked by
  618. ///
  619. /// bool download_complete = (downloaded_bytes == downloadable_bytes);
  620. ///
  621. /// However, download completion might never be reached because the server
  622. /// can receive new changesets from other clients. downloadable_bytes can
  623. /// decrease for two reasons: server side compaction and changesets of
  624. /// local origin. Code using downloadable_bytes must not assume that it
  625. /// is increasing.
  626. ///
  627. /// Upload progress can be calculated by caching an initial value of
  628. /// uploaded_bytes from the last, or next, callback. Then
  629. ///
  630. /// double upload_progress =
  631. /// (uploaded_bytes - initial_uploaded_bytes)
  632. /// -------------------------------------------
  633. /// (uploadable_bytes - initial_uploaded_bytes)
  634. ///
  635. /// Download progress can be calculates similarly:
  636. ///
  637. /// double download_progress =
  638. /// (downloaded_bytes - initial_downloaded_bytes)
  639. /// -----------------------------------------------
  640. /// (downloadable_bytes - initial_downloaded_bytes)
  641. ///
  642. /// progress_version is 0 at the start of a session. When at least one
  643. /// DOWNLOAD message has been received from the server, progress_version is
  644. /// positive. progress_version can be used to ensure that the reported
  645. /// progress contains information obtained from the server in the current
  646. /// session. The server will send a message as soon as possible, and the
  647. /// progress handler will eventually be called with a positive progress_version
  648. /// unless the session is interrupted before a message from the server has
  649. /// been received.
  650. ///
  651. /// The handler is called on the event loop thread.The handler after bind(),
  652. /// after each DOWNLOAD message, and after each local transaction
  653. /// (nonsync_transact_notify).
  654. ///
  655. /// set_progress_handler() is not thread safe and it must be called before
  656. /// bind() is called. Subsequent calls to set_progress_handler() overwrite
  657. /// the previous calls. Typically, this function is called once per session.
  658. ///
  659. /// CAUTION: The specified callback function may get called before the call
  660. /// to bind() returns, and it may get called (or continue to execute) after
  661. /// the session object is destroyed. Please see "Callback semantics" section
  662. /// under Session for more on this.
  663. void set_progress_handler(std::function<ProgressHandler>);
  664. enum class ConnectionState { disconnected, connecting, connected };
  665. /// \brief Information about an error causing a session to be temporarily
  666. /// disconnected from the server.
  667. ///
  668. /// In general, the connection will be automatically reestablished
  669. /// later. Whether this happens quickly, generally depends on \ref
  670. /// is_fatal. If \ref is_fatal is true, it means that the error is deemed to
  671. /// be of a kind that is likely to persist, and cause all future reconnect
  672. /// attempts to fail. In that case, if another attempt is made at
  673. /// reconnecting, the delay will be substantial (at least an hour).
  674. ///
  675. /// \ref error_code specifies the error that caused the connection to be
  676. /// closed. For the list of errors reported by the server, see \ref
  677. /// ProtocolError (or `protocol.md`). For the list of errors corresponding
  678. /// to protocol violations that are detected by the client, see
  679. /// Client::Error. The error may also be a system level error, or an error
  680. /// from one of the potential intermediate protocol layers (SSL or
  681. /// WebSocket).
  682. ///
  683. /// \ref detailed_message is the most detailed message available to describe
  684. /// the error. It is generally equal to `error_code.message()`, but may also
  685. /// be a more specific message (one that provides extra context). The
  686. /// purpose of this message is mostly to aid in debugging. For non-debugging
  687. /// purposes, `error_code.message()` should generally be considered
  688. /// sufficient.
  689. ///
  690. /// \sa set_connection_state_change_listener().
  691. struct ErrorInfo {
  692. std::error_code error_code;
  693. bool is_fatal;
  694. const std::string& detailed_message;
  695. };
  696. using ConnectionStateChangeListener = void(ConnectionState, const ErrorInfo*);
  697. /// \brief Install a connection state change listener.
  698. ///
  699. /// Sets a function to be called whenever the state of the underlying
  700. /// network connection changes between "disconnected", "connecting", and
  701. /// "connected". The initial state is always "disconnected". The next state
  702. /// after "disconnected" is always "connecting". The next state after
  703. /// "connecting" is either "connected" or "disconnected". The next state
  704. /// after "connected" is always "disconnected". A switch to the
  705. /// "disconnected" state only happens when an error occurs.
  706. ///
  707. /// Whenever the installed function is called, an ErrorInfo object is passed
  708. /// when, and only when the passed state is ConnectionState::disconnected.
  709. ///
  710. /// When multiple sessions share a single connection, the state changes will
  711. /// be reported for each session in turn.
  712. ///
  713. /// The callback function will always be called by the thread that executes
  714. /// the event loop (Client::run()), but not until bind() is called. If the
  715. /// callback function throws an exception, that exception will "travel" out
  716. /// through Client::run().
  717. ///
  718. /// Note: Any call to this function must have returned before bind() is
  719. /// called. If this function is called multiple times, each call overrides
  720. /// the previous setting.
  721. ///
  722. /// Note: This function is **not thread-safe**. That is, it is an error if
  723. /// it is called while another thread is executing any member function on
  724. /// the same Session object.
  725. ///
  726. /// CAUTION: The specified callback function may get called before the call
  727. /// to bind() returns, and it may get called (or continue to execute) after
  728. /// the session object is destroyed. Please see "Callback semantics" section
  729. /// under Session for more on this.
  730. void set_connection_state_change_listener(std::function<ConnectionStateChangeListener>);
  731. //@{
  732. /// Deprecated! Use set_connection_state_change_listener() instead.
  733. using ErrorHandler = void(std::error_code, bool is_fatal, const std::string& detailed_message);
  734. void set_error_handler(std::function<ErrorHandler>);
  735. //@}
  736. /// @{ \brief Bind this session to the specified server side Realm.
  737. ///
  738. /// No communication takes place on behalf of this session before the
  739. /// session is bound, but as soon as the session becomes bound, the server
  740. /// will start to push changes to the client, and vice versa.
  741. ///
  742. /// If a callback function was set using set_sync_transact_callback(), then
  743. /// that callback function will start to be called as changesets are
  744. /// downloaded and integrated locally. It is important to understand that
  745. /// callback functions are executed by the event loop thread (Client::run())
  746. /// and the callback function may therefore be called before bind() returns.
  747. ///
  748. /// Note: It is an error if this function is called more than once per
  749. /// Session object.
  750. ///
  751. /// Note: This function is **not thread-safe**. That is, it is an error if
  752. /// it is called while another thread is executing any member function on
  753. /// the same Session object.
  754. ///
  755. /// bind() binds this session to the specified server side Realm using the
  756. /// parameters specified in the Session::Config object.
  757. ///
  758. /// The two other forms of bind() are convenience functions.
  759. void bind();
  760. /// \brief parses parameters and replaces the parameters in the Session::Config object
  761. /// before the session is bound.
  762. /// \param server_url For example "realm://sync.realm.io/test". See
  763. /// server_address, server_path, and server_port in Session::Config for
  764. /// information about the individual components of the URL. See
  765. /// ProtocolEnvelope for the list of available URL schemes and the
  766. /// associated default ports.
  767. ///
  768. /// \throw BadServerUrl if the specified server URL is malformed.
  769. void bind(std::string server_url, std::string signed_user_token);
  770. /// void bind(std::string server_address, std::string server_path,
  771. /// std::string signed_user_token, port_type server_port = 0,
  772. /// ProtocolEnvelope protocol = ProtocolEnvelope::realm);
  773. /// replaces the corresponding parameters from the Session::Config object
  774. /// before the session is bound.
  775. void bind(std::string server_address, std::string server_path, std::string signed_user_token,
  776. port_type server_port = 0, ProtocolEnvelope protocol = ProtocolEnvelope::realm);
  777. /// @}
  778. /// \brief Refresh the access token associated with this session.
  779. ///
  780. /// This causes the REFRESH protocol message to be sent to the server. See
  781. /// ProtocolEnvelope. It is an error to pass a token with a different user
  782. /// identity than the token used to initiate the session.
  783. ///
  784. /// In an on-going session the application may expect the access token to
  785. /// expire at a certain time and schedule acquisition of a fresh access
  786. /// token (using a refresh token or by other means) in due time to provide a
  787. /// better user experience, and seamless connectivity to the server.
  788. ///
  789. /// If the application does not proactively refresh an expiring token, the
  790. /// session will eventually be disconnected. The application can detect this
  791. /// by monitoring the connection state
  792. /// (set_connection_state_change_listener()), and check whether the error
  793. /// code is `ProtocolError::token_expired`. Such a session can then be
  794. /// revived by calling refresh() with a newly acquired access token.
  795. ///
  796. /// Due to protocol techicalities, a race condition exists that can cause a
  797. /// session to become, and remain disconnected after a new access token has
  798. /// been passed to refresh(). The application can work around this race
  799. /// condition by detecting the `ProtocolError::token_expired` error, and
  800. /// always initiate a token renewal in this case.
  801. ///
  802. /// It is an error to call this function before calling `Client::bind()`.
  803. ///
  804. /// Note: This function is thread-safe.
  805. ///
  806. /// \param signed_user_token A cryptographically signed token describing the
  807. /// identity and access rights of the current user. See ProtocolEnvelope.
  808. void refresh(std::string signed_user_token);
  809. /// \brief Inform the synchronization agent about changes of local origin.
  810. ///
  811. /// This function must be called by the application after a transaction
  812. /// performed on its behalf, that is, after a transaction that is not
  813. /// performed to integrate a changeset that was downloaded from the server.
  814. ///
  815. /// It is an error to call this function before bind() has been called, and
  816. /// has returned.
  817. ///
  818. /// Note: This function is fully thread-safe. That is, it may be called by
  819. /// any thread, and by multiple threads concurrently.
  820. void nonsync_transact_notify(version_type new_version);
  821. /// @{ \brief Wait for upload, download, or upload+download completion.
  822. ///
  823. /// async_wait_for_upload_completion() initiates an asynchronous wait for
  824. /// upload to complete, async_wait_for_download_completion() initiates an
  825. /// asynchronous wait for download to complete, and
  826. /// async_wait_for_sync_completion() initiates an asynchronous wait for
  827. /// upload and download to complete.
  828. ///
  829. /// Upload is considered complete when all non-empty changesets of local
  830. /// origin have been uploaded to the server, and the server has acknowledged
  831. /// reception of them. Changesets of local origin introduced after the
  832. /// initiation of the session (after bind() is called) will generally not be
  833. /// considered for upload unless they are announced to this client through
  834. /// nonsync_transact_notify() prior to the initiation of the wait operation,
  835. /// i.e., prior to the invocation of async_wait_for_upload_completion() or
  836. /// async_wait_for_sync_completion(). Unannounced changesets may get picked
  837. /// up, but there is no guarantee that they will be, however, if a certain
  838. /// changeset is announced, then all previous changesets are implicitly
  839. /// announced. Also all preexisting changesets are implicitly announced
  840. /// when the session is initiated.
  841. ///
  842. /// Download is considered complete when all non-empty changesets of remote
  843. /// origin have been downloaded from the server, and integrated into the
  844. /// local Realm state. To know what is currently outstanding on the server,
  845. /// the client always sends a special "marker" message to the server, and
  846. /// waits until it has downloaded all outstanding changesets that were
  847. /// present on the server at the time when the server received that marker
  848. /// message. Each call to async_wait_for_download_completion() and
  849. /// async_wait_for_sync_completion() therefore requires a full client <->
  850. /// server round-trip.
  851. ///
  852. /// If a new wait operation is initiated while another wait operation is in
  853. /// progress by another thread, the waiting period of first operation may,
  854. /// or may not get extended. The application must not assume either. The
  855. /// application may assume, however, that async_wait_for_upload_completion()
  856. /// will not affect the waiting period of
  857. /// async_wait_for_download_completion(), and vice versa.
  858. ///
  859. /// It is an error to call these functions before bind() has been called,
  860. /// and has returned.
  861. ///
  862. /// The specified completion handlers will always be executed by the thread
  863. /// that executes the event loop (the thread that calls Client::run()). If
  864. /// the handler throws an exception, that exception will "travel" out
  865. /// through Client::run().
  866. ///
  867. /// If incomplete wait operations exist when the session is terminated,
  868. /// those wait operations will be canceled. Session termination is an event
  869. /// that happens in the context of the client's event loop thread shortly
  870. /// after the destruction of the session object. The std::error_code
  871. /// argument passed to the completion handler of a canceled wait operation
  872. /// will be `util::error::operation_aborted`. For uncanceled wait operations
  873. /// it will be `std::error_code{}`. Note that as long as the client's event
  874. /// loop thread is running, all completion handlers will be called
  875. /// regardless of whether the operations get canceled or not.
  876. ///
  877. /// CAUTION: The specified completion handlers may get called before the
  878. /// call to the waiting function returns, and it may get called (or continue
  879. /// to execute) after the session object is destroyed. Please see "Callback
  880. /// semantics" section under Session for more on this.
  881. ///
  882. /// Note: These functions are fully thread-safe. That is, they may be called
  883. /// by any thread, and by multiple threads concurrently.
  884. void async_wait_for_sync_completion(WaitOperCompletionHandler);
  885. void async_wait_for_upload_completion(WaitOperCompletionHandler);
  886. void async_wait_for_download_completion(WaitOperCompletionHandler);
  887. /// @}
  888. /// @{ \brief Synchronous wait for upload or download completion.
  889. ///
  890. /// These functions are synchronous equivalents of
  891. /// async_wait_for_upload_completion() and
  892. /// async_wait_for_download_completion() respectively. This means that they
  893. /// block the caller until the completion condition is satisfied, or the
  894. /// client's event loop thread exits from Client::run(), whichever happens
  895. /// first.
  896. ///
  897. /// It is an error to call these functions before bind() has been called,
  898. /// and has returned.
  899. ///
  900. /// CAUTION: If Client::run() returns while a wait operation is in progress,
  901. /// these waiting functions return immediately, even if the completion
  902. /// condition is not yet satisfied. The completion condition is guaranteed
  903. /// to be satisfied only when these functions return true.
  904. ///
  905. /// \return True only if the completion condition was satisfied. False if
  906. /// the client's event loop thread exited from Client::run() in which case
  907. /// the completion condition may, or may not have been satisfied.
  908. ///
  909. /// Note: These functions are fully thread-safe. That is, they may be called
  910. /// by any thread, and by multiple threads concurrently.
  911. bool wait_for_upload_complete_or_client_stopped();
  912. bool wait_for_download_complete_or_client_stopped();
  913. /// @}
  914. /// \brief Cancel the current or next reconnect delay for the server
  915. /// associated with this session.
  916. ///
  917. /// When the network connection is severed, or an attempt to establish
  918. /// connection fails, a certain delay will take effect before the client
  919. /// will attempt to reestablish the connection. This delay will generally
  920. /// grow with the number of unsuccessful reconnect attempts, and can grow to
  921. /// over a minute. In some cases however, the application will know when it
  922. /// is a good time to stop waiting and retry immediately. One example is
  923. /// when a device has been offline for a while, and the operating system
  924. /// then tells the application that network connectivity has been restored.
  925. ///
  926. /// Clearly, this function should not be called too often and over extended
  927. /// periods of time, as that would effectively disable the built-in "server
  928. /// hammering" protection.
  929. ///
  930. /// It is an error to call this function before bind() has been called, and
  931. /// has returned.
  932. ///
  933. /// This function is fully thread-safe. That is, it may be called by any
  934. /// thread, and by multiple threads concurrently.
  935. void cancel_reconnect_delay();
  936. /// \brief Change address of server for this session.
  937. void override_server(std::string address, port_type);
  938. private:
  939. class Impl;
  940. Impl* m_impl = nullptr;
  941. void abandon() noexcept;
  942. void async_wait_for(bool upload_completion, bool download_completion, WaitOperCompletionHandler);
  943. };
  944. /// \brief Protocol errors discovered by the client.
  945. ///
  946. /// These errors will terminate the network connection (disconnect all sessions
  947. /// associated with the affected connection), and the error will be reported to
  948. /// the application via the connection state change listeners of the affected
  949. /// sessions.
  950. enum class Client::Error {
  951. // clang-format off
  952. connection_closed = 100, ///< Connection closed (no error)
  953. unknown_message = 101, ///< Unknown type of input message
  954. bad_syntax = 102, ///< Bad syntax in input message head
  955. limits_exceeded = 103, ///< Limits exceeded in input message
  956. bad_session_ident = 104, ///< Bad session identifier in input message
  957. bad_message_order = 105, ///< Bad input message order
  958. bad_client_file_ident = 106, ///< Bad client file identifier (IDENT)
  959. bad_progress = 107, ///< Bad progress information (DOWNLOAD)
  960. bad_changeset_header_syntax = 108, ///< Bad syntax in changeset header (DOWNLOAD)
  961. bad_changeset_size = 109, ///< Bad changeset size in changeset header (DOWNLOAD)
  962. bad_origin_file_ident = 110, ///< Bad origin file identifier in changeset header (DOWNLOAD)
  963. bad_server_version = 111, ///< Bad server version in changeset header (DOWNLOAD)
  964. bad_changeset = 112, ///< Bad changeset (DOWNLOAD)
  965. bad_request_ident = 113, ///< Bad request identifier (MARK)
  966. bad_error_code = 114, ///< Bad error code (ERROR),
  967. bad_compression = 115, ///< Bad compression (DOWNLOAD)
  968. bad_client_version = 116, ///< Bad last integrated client version in changeset header (DOWNLOAD)
  969. ssl_server_cert_rejected = 117, ///< SSL server certificate rejected
  970. pong_timeout = 118, ///< Timeout on reception of PONG respone message
  971. bad_client_file_ident_salt = 119, ///< Bad client file identifier salt (IDENT)
  972. bad_file_ident = 120, ///< Bad file identifier (ALLOC)
  973. connect_timeout = 121, ///< Sync connection was not fully established in time
  974. bad_timestamp = 122, ///< Bad timestamp (PONG)
  975. bad_protocol_from_server = 123, ///< Bad or missing protocol version information from server
  976. client_too_old_for_server = 124, ///< Protocol version negotiation failed: Client is too old for server
  977. client_too_new_for_server = 125, ///< Protocol version negotiation failed: Client is too new for server
  978. protocol_mismatch = 126, ///< Protocol version negotiation failed: No version supported by both client and server
  979. bad_state_message = 127, ///< Bad values in state message (STATE)
  980. missing_protocol_feature = 128, ///< Requested feature missing in negotiated protocol version
  981. http_tunnel_failed = 131, ///< Failed to establish HTTP tunnel with configured proxy
  982. // clang-format on
  983. };
  984. const std::error_category& client_error_category() noexcept;
  985. std::error_code make_error_code(Client::Error) noexcept;
  986. std::ostream& operator<<(std::ostream& os, SyncConfig::ProxyConfig::Type);
  987. } // namespace sync
  988. } // namespace realm
  989. namespace std {
  990. template <>
  991. struct is_error_code_enum<realm::sync::Client::Error> {
  992. static const bool value = true;
  993. };
  994. } // namespace std
  995. namespace realm {
  996. namespace sync {
  997. // Implementation
  998. class BadServerUrl : public std::exception {
  999. public:
  1000. const char* what() const noexcept override
  1001. {
  1002. return "Bad server URL";
  1003. }
  1004. };
  1005. inline Session::Session(Session&& sess) noexcept
  1006. : m_impl{sess.m_impl}
  1007. {
  1008. sess.m_impl = nullptr;
  1009. }
  1010. inline Session::Session() noexcept {}
  1011. inline Session::~Session() noexcept
  1012. {
  1013. if (m_impl)
  1014. abandon();
  1015. }
  1016. inline Session& Session::operator=(Session&& sess) noexcept
  1017. {
  1018. if (m_impl)
  1019. abandon();
  1020. m_impl = sess.m_impl;
  1021. sess.m_impl = nullptr;
  1022. return *this;
  1023. }
  1024. inline void Session::detach() noexcept
  1025. {
  1026. if (m_impl)
  1027. abandon();
  1028. m_impl = nullptr;
  1029. }
  1030. inline void Session::set_error_handler(std::function<ErrorHandler> handler)
  1031. {
  1032. auto handler_2 = [handler = std::move(handler)](ConnectionState state, const ErrorInfo* error_info) {
  1033. if (state != ConnectionState::disconnected)
  1034. return;
  1035. REALM_ASSERT(error_info);
  1036. std::error_code ec = error_info->error_code;
  1037. bool is_fatal = error_info->is_fatal;
  1038. const std::string& detailed_message = error_info->detailed_message;
  1039. handler(ec, is_fatal, detailed_message); // Throws
  1040. };
  1041. set_connection_state_change_listener(std::move(handler_2)); // Throws
  1042. }
  1043. inline void Session::async_wait_for_sync_completion(WaitOperCompletionHandler handler)
  1044. {
  1045. bool upload_completion = true, download_completion = true;
  1046. async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
  1047. }
  1048. inline void Session::async_wait_for_upload_completion(WaitOperCompletionHandler handler)
  1049. {
  1050. bool upload_completion = true, download_completion = false;
  1051. async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
  1052. }
  1053. inline void Session::async_wait_for_download_completion(WaitOperCompletionHandler handler)
  1054. {
  1055. bool upload_completion = false, download_completion = true;
  1056. async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
  1057. }
  1058. } // namespace sync
  1059. } // namespace realm
  1060. #endif // REALM_SYNC_CLIENT_HPP