1 /*************************************************************************
6 * [2011] - [2012] Realm Inc
9 * NOTICE: All information contained herein is, and remains
10 * the property of Realm Incorporated and its suppliers,
11 * if any. The intellectual and technical concepts contained
12 * herein are proprietary to Realm Incorporated
13 * and its suppliers and may be covered by U.S. and Foreign Patents,
14 * patents in process, and are protected by trade secret or copyright law.
15 * Dissemination of this information or reproduction of this material
16 * is strictly forbidden unless prior written permission is obtained
17 * from Realm Incorporated.
19 **************************************************************************/
20 #ifndef REALM_SYNC_CLIENT_HPP
21 #define REALM_SYNC_CLIENT_HPP
30 #include <realm/util/logger.hpp>
31 #include <realm/util/network.hpp>
32 #include <realm/impl/cont_transact_hist.hpp>
33 #include <realm/sync/history.hpp>
42 enum class ReconnectMode {
43 /// This is the mode that should always be used in production. In this
44 /// mode the client uses a scheme for determining a reconnect delay that
45 /// prevents it from creating too many connection requests in a short
46 /// amount of time (i.e., a server hammering protection mechanism).
49 /// Delay reconnect attempts indefinitely. For testing purposes only.
51 /// A reconnect attempt can be manually scheduled by calling
52 /// cancel_reconnect_delay(). In particular, when a connection breaks,
53 /// or when an attempt at establishing the connection fails, the
54 /// connection state change listener is called. If one calls
55 /// cancel_reconnect_delay() from that invocation of the listener, the
56 /// effect is to allow another reconnect attempt to occur.
59 /// Never delay reconnect attempts. Perform them immediately. For
60 /// testing purposes only.
64 static constexpr std::uint_fast64_t default_ping_keepalive_period_ms = 600000; // 10 minutes
65 static constexpr std::uint_fast64_t default_pong_keepalive_timeout_ms = 300000; // 5 minutes
66 static constexpr std::uint_fast64_t default_pong_urgent_timeout_ms = 5000; // 5 seconds
71 /// The maximum number of Realm files that will be kept open
72 /// concurrently by this client. The client keeps a cache of open Realm
73 /// files for efficiency reasons.
74 long max_open_files = 256;
76 /// An optional logger to be used by the client. If no logger is
77 /// specified, the client will use an instance of util::StderrLogger
78 /// with the log level threshold set to util::Logger::Level::info. The
79 /// client does not require a thread-safe logger, and it guarantees that
80 /// all logging happens either on behalf of the constructor or on behalf
81 /// of the invocation of run().
82 util::Logger* logger = nullptr;
84 /// Use ports 80 and 443 by default instead of 7800 and 7801
85 /// respectively. Ideally, these default ports should have been made
86 /// available via a different URI scheme instead (http/https or ws/wss).
87 bool enable_default_port_hack = true;
89 /// For testing purposes only.
90 ReconnectMode reconnect_mode = ReconnectMode::normal;
92 /// Create a separate connection for each session. For testing purposes
95 /// FIXME: This setting needs to be true for now, due to limitations in
96 /// the load balancer.
97 bool one_connection_per_session = true;
99 /// Do not access the local file system. Sessions will act as if
100 /// initiated on behalf of an empty (or nonexisting) local Realm
101 /// file. Received DOWNLOAD messages will be accepted, but otherwise
102 /// ignored. No UPLOAD messages will be generated. For testing purposes
104 bool dry_run = false;
106 /// The default changeset cooker to be used by new sessions. Can be
107 /// overridden by Session::Config::changeset_cooker.
109 /// \sa make_client_history(), TrivialChangesetCooker.
110 std::shared_ptr<ClientHistory::ChangesetCooker> changeset_cooker;
112 /// The number of ms between periodic keep-alive pings.
113 std::uint_fast64_t ping_keepalive_period_ms = default_ping_keepalive_period_ms;
115 /// The number of ms to wait for keep-alive pongs.
116 std::uint_fast64_t pong_keepalive_timeout_ms = default_pong_keepalive_timeout_ms;
118 /// The number of ms to wait for urgent pongs.
119 std::uint_fast64_t pong_urgent_timeout_ms = default_pong_urgent_timeout_ms;
121 /// If enable_upload_log_compaction is true, every changeset will be
122 /// compacted before it is uploaded to the server. Compaction will
123 /// reduce the size of a changeset if the same field is set multiple
124 /// times or if newly created objects are deleted within the same
125 /// transaction. Log compaction increeses CPU usage and memory
127 bool enable_upload_log_compaction = true;
129 /// Set the `TCP_NODELAY` option on all TCP/IP sockets. This disables
130 /// the Nagle algorithm. Disabling it, can in some cases be used to
131 /// decrease latencies, but possibly at the expense of scalability. Be
132 /// sure to research the subject before you enable this option.
133 bool tcp_no_delay = false;
136 /// \throw util::EventLoop::Implementation::NotAvailable if no event loop
137 /// implementation was specified, and
138 /// util::EventLoop::Implementation::get_default() throws it.
140 Client(Client&&) noexcept;
143 /// Run the internal event-loop of the client. At most one thread may
144 /// execute run() at any given time. The call will not return until somebody
151 void stop() noexcept;
153 /// \brief Cancel current or next reconnect delay for all servers.
155 /// This corresponds to calling Session::cancel_reconnect_delay() on all
156 /// bound sessions, but will also cancel reconnect delays applying to
157 /// servers for which there are currently no bound sessions.
160 void cancel_reconnect_delay();
162 /// \brief Wait for session termination to complete.
164 /// Wait for termination of all sessions whose termination was initiated
165 /// prior this call (the completion condition), or until the client's event
166 /// loop thread exits from Client::run(), whichever happens
167 /// first. Termination of a session can be initiated implicitly (e.g., via
168 /// destruction of the session object), or explicitly by Session::detach().
170 /// Note: After session termination (when this function returns true) no
171 /// session specific callback function can be called or continue to execute,
172 /// and the client is guaranteed to no longer have a Realm file open on
173 /// behalf of the terminated session.
175 /// CAUTION: If run() returns while a wait operation is in progress, this
176 /// waiting function will return immediately, even if the completion
177 /// condition is not yet satisfied. The completion condition is guaranteed
178 /// to be satisfied only when these functions return true. If it returns
179 /// false, session specific callback functions may still be executing or get
180 /// called, and the associated Realm files may still not have been closed.
182 /// If a new wait operation is initiated while another wait operation is in
183 /// progress by another thread, the waiting period of fist operation may, or
184 /// may not get extended. The application must not assume either.
186 /// Note: Session termination does not imply that the client has received an
187 /// UNBOUND message from the server (see the protocol specification). This
188 /// may happen later.
190 /// \return True only if the completion condition was satisfied. False if
191 /// the client's event loop thread exited from Client::run() in which case
192 /// the completion condition may, or may not have been satisfied.
194 /// Note: These functions are fully thread-safe. That is, they may be called
195 /// by any thread, and by multiple threads concurrently.
196 bool wait_for_session_terminations_or_client_stopped();
200 std::unique_ptr<Impl> m_impl;
201 friend class Session;
205 /// Supported protocols:
207 /// Protocol URL scheme Default port
208 /// -----------------------------------------------------------------------------------
209 /// realm "realm:" 7800 (80 if Client::Config::enable_default_port_hack)
210 /// realm_ssl "realms:" 7801 (443 if Client::Config::enable_default_port_hack)
212 enum class Protocol {
218 class BadServerUrl; // Exception
221 /// \brief Client-side representation of a Realm file synchronization session.
223 /// A synchronization session deals with precisely one local Realm file. To
224 /// synchronize multiple local Realm files, you need multiple sessions.
226 /// A session object is always associated with a particular client object (\ref
227 /// Client). The application must ensure that the destruction of the associated
228 /// client object never happens before the destruction of the session
229 /// object. The consequences of a violation are unspecified.
231 /// A session object is always associated with a particular local Realm file,
232 /// however, a session object does not represent a session until it is bound to
233 /// a server side Realm, i.e., until bind() is called. From the point of view of
234 /// the thread that calls bind(), the session starts precisely when the
235 /// execution of bind() starts, i.e., before bind() returns.
237 /// At most one session is allowed to exist for a particular local Realm file
238 /// (file system inode) at any point in time. Multiple session objects may
239 /// coexists for a single file, as long as bind() has been called on at most one
240 /// of them. Additionally, two bound session objects for the same file are
241 /// allowed to exist at different times, if they have no overlap in time (in
242 /// their bound state), as long as they are associated with the same client
243 /// object, or with two different client objects that do not overlap in
244 /// time. This means, in particular, that it is an error to create two bound
245 /// session objects for the same local Realm file, it they are associated with
246 /// two different client objects that overlap in time, even if the session
247 /// objects do not overlap in time (in their bound state). It is the
248 /// responsibility of the application to ensure that these rules are adhered
249 /// to. The consequences of a violation are unspecified.
251 /// Thread-safety: It is safe for multiple threads to construct, use (with some
252 /// exceptions), and destroy session objects concurrently, regardless of whether
253 /// those session objects are associated with the same, or with different Client
254 /// objects. Please note that some of the public member functions are fully
255 /// thread-safe, while others are not.
257 /// Callback semantics: All session specific callback functions will be executed
258 /// by the event loop thread, i.e., the thread that calls Client::run(). No
259 /// callback function will be called before Session::bind() is called. Callback
260 /// functions that are specified prior to calling bind() (e.g., any passed to
261 /// set_progress_handler()) may start to execute before bind() returns, as long
262 /// as some thread is executing Client::run(). Likewise, completion handlers,
263 /// such as those passed to async_wait_for_sync_completion() may start to
264 /// execute before the submitting function returns. All session specific
265 /// callback functions (including completion handlers) are guaranteed to no
266 /// longer be executing when session termination completes, and they are
267 /// guaranteed to not be called after session termination completes. Termination
268 /// is an event that completes asynchronously with respect to the application,
269 /// but is initiated by calling detach(), or implicitely by destroying a session
270 /// object. After having initiated one or more session terminations, the
271 /// application can wait for those terminations to complete by calling
272 /// Client::wait_for_session_terminations_or_client_stopped(). Since callback
273 /// functinos are always executed by the event loop thread, they are also
274 /// guaranteed to not be executing after Client::run() has returned.
277 using port_type = util::network::Endpoint::port_type;
278 using version_type = _impl::History::version_type;
279 using SyncTransactCallback = void(VersionID old_version, VersionID new_version);
280 using ProgressHandler = void(std::uint_fast64_t downloaded_bytes,
281 std::uint_fast64_t downloadable_bytes,
282 std::uint_fast64_t uploaded_bytes,
283 std::uint_fast64_t uploadable_bytes,
284 std::uint_fast64_t progress_version,
285 std::uint_fast64_t snapshot_version);
286 using WaitOperCompletionHandler = std::function<void(std::error_code)>;
287 using SSLVerifyCallback = bool(const std::string& server_address,
288 port_type server_port,
289 const char* pem_data,
298 /// server_address is the fully qualified host name, or IP address of
300 std::string server_address = "localhost";
302 /// server_port is the port at which the server listens. If server_port
303 /// is zero, the default port for the specified protocol is used. See \ref
304 /// Protocol for information on default ports.
305 port_type server_port = 0;
307 /// server_path is the virtual path by which the server identifies the
308 /// Realm. This path must always be an absolute path, and must therefore
309 /// always contain a leading slash (`/`). Further more, each segment of the
310 /// virtual path must consist of one or more characters that are either
311 /// alpha-numeric or in (`_`, `-`, `.`), and each segment is not allowed to
312 /// equal `.` or `..`, and must not end with `.realm`, `.realm.lock`, or
313 /// `.realm.management`. These rules are necessary because the server
314 /// currently reserves the right to use the specified path as part of the
315 /// file system path of a Realm file. It is expected that these rules will
316 /// be significantly relaxed in the future by completely decoupling the
317 /// virtual paths from actual file system paths.
318 std::string server_path = "/";
320 /// The protocol used for communicating with the server. See \ref Protocol.
321 Protocol protocol = Protocol::realm;
323 /// url_prefix is a prefix that is prepended to the server_path
324 /// in the HTTP GET request that initiates a sync connection. The value
325 /// specified here must match with the server's expectation. Changing
326 /// the value of url_prefix should be matched with a corresponding
327 /// change of the server side proxy.
328 std::string url_prefix = "/realm-sync";
330 /// Sessions can be multiplexed over the same TCP/SSL connection.
331 /// Sessions might share connection if they have identical server_address,
332 /// server_port, and protocol. multiplex_ident is a parameter that allows
333 /// finer control over session multiplexing. If two sessions have distinct
334 /// multiplex_ident, they will never share connection. The typical use of
335 /// multilex_ident is to give sessions with incompatible SSL requirements
336 /// distinct multiplex_idents.
337 /// multiplex_ident can be any string and the value has no meaning except
338 /// for partitioning the sessions.
339 std::string multiplex_ident;
341 /// verify_servers_ssl_certificate controls whether the server
342 /// certificate is verified for SSL connections. It should generally be
343 /// true in production.
344 bool verify_servers_ssl_certificate = true;
346 /// ssl_trust_certificate_path is the path of a trust/anchor
347 /// certificate used by the client to verify the server certificate.
348 /// ssl_trust_certificate_path is only used if the protocol is ssl and
349 /// verify_servers_ssl_certificate is true.
351 /// A server certificate is verified by first checking that the
352 /// certificate has a valid signature chain back to a trust/anchor
353 /// certificate, and secondly checking that the server_address matches
354 /// a host name contained in the certificate. The host name of the
355 /// certificate is stored in either Common Name or the Alternative
356 /// Subject Name (DNS section).
358 /// If ssl_trust_certificate_path is None (default), ssl_verify_callback
359 /// (see below) is used if set, and the default device trust/anchor
360 /// store is used otherwise.
361 Optional<std::string> ssl_trust_certificate_path;
363 /// If Client::Config::ssl_verify_callback is set, that function is called
364 /// to verify the certificate, unless verify_servers_ssl_certificate is
367 /// ssl_verify_callback is used to implement custom SSL certificate
368 /// verification. it is only used if the protocol is SSL,
369 /// verify_servers_ssl_certificate is true and ssl_trust_certificate_path
372 /// The signature of ssl_verify_callback is
374 /// bool(const std::string& server_address,
375 /// port_type server_port,
376 /// const char* pem_data,
378 /// int preverify_ok,
381 /// server address and server_port is the address and port of the server
382 /// that a SSL connection is being established to. They are identical to
383 /// the server_address and server_port set in this config file and are
384 /// passed for convenience.
385 /// pem_data is the certificate of length pem_size in
386 /// the PEM format. preverify_ok is OpenSSL's preverification of the
387 /// certificate. preverify_ok is either 0, or 1. If preverify_ok is 1,
388 /// OpenSSL has accepted the certificate and it will generally be safe
389 /// to trust that certificate. depth represents the position of the
390 /// certificate in the certificate chain sent by the server. depth = 0
391 /// represents the actual server certificate that should contain the
392 /// host name(server address) of the server. The highest depth is the
393 /// root certificate.
394 /// The callback function will receive the certificates starting from
395 /// the root certificate and moving down the chain until it reaches the
396 /// server's own certificate with a host name. The depth of the last
397 /// certificate is 0. The depth of the first certificate is chain
400 /// The return value of the callback function decides whether the
401 /// client accepts the certificate. If the return value is false, the
402 /// processing of the certificate chain is interrupted and the SSL
403 /// connection is rejected. If the return value is true, the verification
404 /// process continues. If the callback function returns true for all
405 /// presented certificates including the depth == 0 certificate, the
406 /// SSL connection is accepted.
408 /// A recommended way of using the callback function is to return true
409 /// if preverify_ok = 1 and depth > 0,
410 /// always check the host name if depth = 0,
411 /// and use an independent verification step if preverify_ok = 0.
413 /// Another possible way of using the callback is to collect all the
414 /// certificates until depth = 0, and present the entire chain for
415 /// independent verification.
416 std::function<SSLVerifyCallback> ssl_verify_callback;
418 /// signed_user_token is a cryptographically signed token describing the
419 /// identity and access rights of the current user.
420 std::string signed_user_token;
422 /// If not null, overrides whatever is specified by
423 /// Client::Config::changeset_cooker.
425 /// The shared ownership over the cooker will be relinquished shortly
426 /// after the destruction of the session object as long as the event
427 /// loop of the client is being executed (Client::run()).
429 /// CAUTION: ChangesetCooker::cook_changeset() of the specified cooker
430 /// may get called before the call to bind() returns, and it may get
431 /// called (or continue to execute) after the session object is
432 /// destroyed. Please see "Callback semantics" section under Client for
435 /// \sa make_client_history(), TrivialChangesetCooker.
436 std::shared_ptr<ClientHistory::ChangesetCooker> changeset_cooker;
438 /// The encryption key the SharedGroup will be opened with.
439 Optional<std::array<char, 64>> encryption_key;
441 /// FIXME: This value must currently be true in a cluster setup.
442 /// This restriction will be lifted in the future.
443 bool one_connection_per_session = true;
446 /// \brief Start a new session for the specified client-side Realm.
448 /// Note that the session is not fully activated until you call bind(). Also
449 /// note that if you call set_sync_transact_callback(), it must be done
450 /// before calling bind().
452 /// \param realm_path The file-system path of a local client-side Realm
454 Session(Client&, std::string realm_path, Config = {});
456 /// This leaves the right-hand side session object detached. See "Thread
457 /// safety" section under detach().
458 Session(Session&&) noexcept;
460 /// Create a detached session object (see detach()).
463 /// Implies detachment. See "Thread safety" section under detach().
466 /// Detach the object on the left-hand side, then "steal" the session from
467 /// the object on the right-hand side, if there is one. This leaves the
468 /// object on the right-hand side detached. See "Thread safety" section
470 Session& operator=(Session&&) noexcept;
472 /// Detach this sesion object from the client object (Client). If the
473 /// session object is already detached, this function has no effect
476 /// Detachment initiates session termination, which is an event that takes
477 /// place shortly therafter in the context of the client's event loop
480 /// A detached session object may be destroyed, move-assigned to, and moved
481 /// from. Apart from that, it is an error to call any function other than
482 /// detach() on a detached session object.
484 /// Thread safety: Detachment is not a thread-safe operation. This means
485 /// that detach() may not be executed by two threads concurrently, and may
486 /// not execute concurrently with object destruction. Additionally,
487 /// detachment must not execute concurrently with a moving operation
488 /// involving the session object on the left or right-hand side. See move
489 /// constructor and assigment operator.
490 void detach() noexcept;
492 /// \brief Set a function to be called when the local Realm has changed due
493 /// to integration of a downloaded changeset.
495 /// Specify the callback function that will be called when one or more
496 /// transactions are performed to integrate downloaded changesets into the
497 /// client-side Realm, that is associated with this session.
499 /// The callback function will always be called by the thread that executes
500 /// the event loop (Client::run()), but not until bind() is called. If the
501 /// callback function throws an exception, that exception will "travel" out
502 /// through Client::run().
504 /// Note: Any call to this function must have returned before bind() is
505 /// called. If this function is called multiple times, each call overrides
506 /// the previous setting.
508 /// Note: This function is **not thread-safe**. That is, it is an error if
509 /// it is called while another thread is executing any member function on
510 /// the same Session object.
512 /// CAUTION: The specified callback function may get called before the call
513 /// to bind() returns, and it may get called (or continue to execute) after
514 /// the session object is destroyed. Please see "Callback semantics" section
515 /// under Session for more on this.
516 void set_sync_transact_callback(std::function<SyncTransactCallback>);
518 /// \brief Set a handler to monitor the state of download and upload
521 /// The handler must have signature
523 /// void(uint_fast64_t downloaded_bytes, uint_fast64_t downloadable_bytes,
524 /// uint_fast64_t uploaded_bytes, uint_fast64_t uploadable_bytes,
525 /// uint_fast64_t progress_version);
527 /// downloaded_bytes is the size in bytes of all downloaded changesets.
528 /// downloadable_bytes is the size in bytes of the part of the server
529 /// history that do not originate from this client.
531 /// uploaded_bytes is the size in bytes of all locally produced changesets
532 /// that have been received and acknowledged by the server.
533 /// uploadable_bytes is the size in bytes of all locally produced changesets.
535 /// Due to the nature of the merge rules, it is possible that the size of an
536 /// uploaded changeset uploaded from one client is not equal to the size of
537 /// the changesets that other clients will download.
539 /// Typical uses of this function:
541 /// Upload completion can be checked by
543 /// bool upload_complete = (uploaded_bytes == uploadable_bytes);
545 /// Download completion could be checked by
547 /// bool download_complete = (downloaded_bytes == downloadable_bytes);
549 /// However, download completion might never be reached because the server
550 /// can receive new changesets from other clients.
551 /// An alternative strategy is to cache downloadable_bytes from the callback,
552 /// and use the cached value as the threshold.
554 /// bool download_complete = (downloaded_bytes == cached_downloadable_bytes);
556 /// Upload progress can be calculated by caching an initial value of
557 /// uploaded_bytes from the last, or next, callback. Then
559 /// double upload_progress =
560 /// (uploaded_bytes - initial_uploaded_bytes)
561 /// -------------------------------------------
562 /// (uploadable_bytes - initial_uploaded_bytes)
564 /// Download progress can be calculates similarly:
566 /// double download_progress =
567 /// (downloaded_bytes - initial_downloaded_bytes)
568 /// -----------------------------------------------
569 /// (downloadable_bytes - initial_downloaded_bytes)
571 /// progress_version is 0 at the start of a session. When at least one
572 /// DOWNLOAD message has been received from the server, progress_version is
573 /// positive. progress_version can be used to ensure that the reported
574 /// progress contains information obtained from the server in the current
575 /// session. The server will send a message as soon as possible, and the
576 /// progress handler will eventually be called with a positive progress_version
577 /// unless the session is interrupted before a message from the server has
580 /// The handler is called on the event loop thread.The handler after bind(),
581 /// after each DOWNLOAD message, and after each local transaction
582 /// (nonsync_transact_notify).
584 /// set_progress_handler() is not thread safe and it must be called before
585 /// bind() is called. Subsequent calls to set_progress_handler() overwrite
586 /// the previous calls. Typically, this function is called once per session.
588 /// CAUTION: The specified callback function may get called before the call
589 /// to bind() returns, and it may get called (or continue to execute) after
590 /// the session object is destroyed. Please see "Callback semantics" section
591 /// under Session for more on this.
592 void set_progress_handler(std::function<ProgressHandler>);
594 enum class ConnectionState { disconnected, connecting, connected };
596 /// \brief Information about an error causing a session to be temporarily
597 /// disconnected from the server.
599 /// In general, the connection will be automatically reestablished
600 /// later. Whether this happens quickly, generally depends on \ref
601 /// is_fatal. If \ref is_fatal is true, it means that the error is deemed to
602 /// be of a kind that is likely to persist, and cause all future reconnect
603 /// attempts to fail. In that case, if another attempt is made at
604 /// reconnecting, the delay will be substantial (at least an hour).
606 /// \ref error_code specifies the error that caused the connection to be
607 /// closed. For the list of errors reported by the server, see \ref
608 /// ProtocolError (or `protocol.md`). For the list of errors corresponding
609 /// to protocol violations that are detected by the client, see
610 /// Client::Error. The error may also be a system level error, or an error
611 /// from one of the potential intermediate protocol layers (SSL or
614 /// \ref detailed_message is the most detailed message available to describe
615 /// the error. It is generally equal to `error_code.message()`, but may also
616 /// be a more specific message (one that provides extra context). The
617 /// purpose of this message is mostly to aid in debugging. For non-debugging
618 /// purposes, `error_code.message()` should generally be considered
621 /// \sa set_connection_state_change_listener().
623 std::error_code error_code;
625 const std::string& detailed_message;
628 using ConnectionStateChangeListener = void(ConnectionState, const ErrorInfo*);
630 /// \brief Install a connection state change listener.
632 /// Sets a function to be called whenever the state of the underlying
633 /// network connection changes between "disconnected", "connecting", and
634 /// "connected". The initial state is always "disconnected". The next state
635 /// after "disconnected" is always "connecting". The next state after
636 /// "connecting" is either "connected" or "disconnected". The next state
637 /// after "connected" is always "disconnected". A switch to the
638 /// "disconnected" state only happens when an error occurs.
640 /// Whenever the installed function is called, an ErrorInfo object is passed
641 /// when, and only when the passed state is ConnectionState::disconnected.
643 /// When multiple sessions share a single connection, the state changes will
644 /// be reported for each session in turn.
646 /// The callback function will always be called by the thread that executes
647 /// the event loop (Client::run()), but not until bind() is called. If the
648 /// callback function throws an exception, that exception will "travel" out
649 /// through Client::run().
651 /// Note: Any call to this function must have returned before bind() is
652 /// called. If this function is called multiple times, each call overrides
653 /// the previous setting.
655 /// Note: This function is **not thread-safe**. That is, it is an error if
656 /// it is called while another thread is executing any member function on
657 /// the same Session object.
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_connection_state_change_listener(std::function<ConnectionStateChangeListener>);
666 /// Deprecated! Use set_connection_state_change_listener() instead.
667 using ErrorHandler = void(std::error_code, bool is_fatal, const std::string& detailed_message);
668 void set_error_handler(std::function<ErrorHandler>);
671 /// @{ \brief Bind this session to the specified server side Realm.
673 /// No communication takes place on behalf of this session before the
674 /// session is bound, but as soon as the session becomes bound, the server
675 /// will start to push changes to the client, and vice versa.
677 /// If a callback function was set using set_sync_transact_callback(), then
678 /// that callback function will start to be called as changesets are
679 /// downloaded and integrated locally. It is important to understand that
680 /// callback functions are executed by the event loop thread (Client::run())
681 /// and the callback function may therefore be called before bind() returns.
683 /// Note: It is an error if this function is called more than once per
686 /// Note: This function is **not thread-safe**. That is, it is an error if
687 /// it is called while another thread is executing any member function on
688 /// the same Session object.
690 /// bind() binds this session to the specified server side Realm using the
691 /// parameters specified in the Session::Config object.
693 /// The two other forms of bind() are convenience functions.
694 /// void bind(std::string server_address, std::string server_path,
695 /// std::string signed_user_token, port_type server_port = 0,
696 /// Protocol protocol = Protocol::realm);
697 /// replaces the corresponding parameters from the Session::Config object
698 /// before the session is bound.
699 /// void bind(std::string server_url, std::string signed_user_token) parses
700 /// the \param server_url and replaces the parameters in the Session::Config object
701 /// before the session is bound.
703 /// \param server_url For example "realm://sync.realm.io/test". See
704 /// server_address, server_path, and server_port in Session::Config for information
705 /// about the individual components of the URL. See \ref Protocol for the list of
706 /// available URL schemes and the associated default ports.
708 /// \throw BadServerUrl if the specified server URL is malformed.
710 void bind(std::string server_url, std::string signed_user_token);
711 void bind(std::string server_address, std::string server_path,
712 std::string signed_user_token, port_type server_port = 0,
713 Protocol protocol = Protocol::realm);
716 /// \brief Refresh the user token associated with this session.
718 /// This causes the REFRESH protocol message to be sent to the server. See
721 /// In an on-going session a client may expect its access token to expire at
722 /// a certain time and schedule acquisition of a fresh access token (using a
723 /// refresh token or by other means) in due time to provide a better user
724 /// experience. Without refreshing the token, the client will be notified
725 /// that the session is terminated due to insufficient privileges and must
726 /// reacquire a fresh token, which is a potentially disruptive process.
728 /// It is an error to call this function before calling `Client::bind()`.
730 /// Note: This function is thread-safe.
732 /// \param signed_user_token A cryptographically signed token describing the
733 /// identity and access rights of the current user. See \ref Protocol.
734 void refresh(std::string signed_user_token);
736 /// \brief Inform the synchronization agent about changes of local origin.
738 /// This function must be called by the application after a transaction
739 /// performed on its behalf, that is, after a transaction that is not
740 /// performed to integrate a changeset that was downloaded from the server.
742 /// It is an error to call this function before bind() has been called, and
745 /// Note: This function is fully thread-safe. That is, it may be called by
746 /// any thread, and by multiple threads concurrently.
747 void nonsync_transact_notify(version_type new_version);
749 /// @{ \brief Wait for upload, download, or upload+download completion.
751 /// async_wait_for_upload_completion() initiates an asynchronous wait for
752 /// upload to complete, async_wait_for_download_completion() initiates an
753 /// asynchronous wait for download to complete, and
754 /// async_wait_for_sync_completion() initiates an asynchronous wait for
755 /// upload and download to complete.
757 /// Upload is considered complete when all non-empty changesets of local
758 /// origin have been uploaded to the server, and the server has acknowledged
759 /// reception of them. Changesets of local origin introduced after the
760 /// initiation of the session (after bind() is called) will generally not be
761 /// considered for upload unless they are announced to this client through
762 /// nonsync_transact_notify() prior to the initiation of the wait operation,
763 /// i.e., prior to the invocation of async_wait_for_upload_completion() or
764 /// async_wait_for_sync_completion(). Unannounced changesets may get picked
765 /// up, but there is no guarantee that they will be, however, if a certain
766 /// changeset is announced, then all previous changesets are implicitly
767 /// announced. Also all preexisting changesets are implicitly announced
768 /// when the session is initiated.
770 /// Download is considered complete when all non-empty changesets of remote
771 /// origin have been downloaded from the server, and integrated into the
772 /// local Realm state. To know what is currently outstanding on the server,
773 /// the client always sends a special "marker" message to the server, and
774 /// waits until it has downloaded all outstanding changesets that were
775 /// present on the server at the time when the server received that marker
776 /// message. Each call to async_wait_for_download_completion() and
777 /// async_wait_for_sync_completion() therefore requires a full client <->
778 /// server round-trip.
780 /// If a new wait operation is initiated while another wait operation is in
781 /// progress by another thread, the waiting period of first operation may,
782 /// or may not get extended. The application must not assume either. The
783 /// application may assume, however, that async_wait_for_upload_completion()
784 /// will not affect the waiting period of
785 /// async_wait_for_download_completion(), and vice versa.
787 /// It is an error to call these functions before bind() has been called,
788 /// and has returned.
790 /// The specified completion handlers will always be executed by the thread
791 /// that executes the event loop (the thread that calls Client::run()). If
792 /// the handler throws an exception, that exception will "travel" out
793 /// through Client::run().
795 /// If incomplete wait operations exist when the session is terminated,
796 /// those wait operations will be canceled. Session termination is an event
797 /// that happens in the context of the client's event loop thread shortly
798 /// after the destruction of the session object. The std::error_code
799 /// argument passed to the completion handler of a canceled wait operation
800 /// will be `util::error::operation_aborted`. For uncanceled wait operations
801 /// it will be `std::error_code{}`. Note that as long as the client's event
802 /// loop thread is running, all completion handlers will be called
803 /// regardless of whether the operations get canceled or not.
805 /// CAUTION: The specified completion handlers may get called before the
806 /// call to the waiting function returns, and it may get called (or continue
807 /// to execute) after the session object is destroyed. Please see "Callback
808 /// semantics" section under Session for more on this.
810 /// Note: These functions are fully thread-safe. That is, they may be called
811 /// by any thread, and by multiple threads concurrently.
812 void async_wait_for_sync_completion(WaitOperCompletionHandler);
813 void async_wait_for_upload_completion(WaitOperCompletionHandler);
814 void async_wait_for_download_completion(WaitOperCompletionHandler);
817 /// @{ \brief Synchronous wait for upload or download completion.
819 /// These functions are synchronous equivalents to
820 /// async_wait_for_upload_completion() and
821 /// async_wait_for_download_completion() respectively. This means that they
822 /// block the caller until the completion condition is satisfied, or the
823 /// client's event loop thread exits from Client::run(), whichever happens
826 /// It is an error to call these functions before bind() has been called,
827 /// and has returned.
829 /// CAUTION: If Client::run() returns while a wait operation is in progress,
830 /// these waiting functions return immediately, even if the completion
831 /// condition is not yet satisfied. The completion condition is guaranteed
832 /// to be satisfied only when these functions return true.
834 /// \return True only if the completion condition was satisfied. False if
835 /// the client's event loop thread exited from Client::run() in which case
836 /// the completion condition may, or may not have been satisfied.
838 /// Note: These functions are fully thread-safe. That is, they may be called
839 /// by any thread, and by multiple threads concurrently.
840 bool wait_for_upload_complete_or_client_stopped();
841 bool wait_for_download_complete_or_client_stopped();
844 /// \brief Cancel the current or next reconnect delay for the server
845 /// associated with this session.
847 /// When the network connection is severed, or an attempt to establish
848 /// connection fails, a certain delay will take effect before the client
849 /// will attempt to reestablish the connection. This delay will generally
850 /// grow with the number of unsuccessful reconnect attempts, and can grow to
851 /// over a minute. In some cases however, the application will know when it
852 /// is a good time to stop waiting and retry immediately. One example is
853 /// when a device has been offline for a while, and the operating system
854 /// then tells the application that network connectivity has been restored.
856 /// Clearly, this function should not be called too often and over extended
857 /// periods of time, as that would effectively disable the built-in "server
858 /// hammering" protection.
860 /// It is an error to call this function before bind() has been called, and
863 /// This function is fully thread-safe. That is, it may be called by any
864 /// thread, and by multiple threads concurrently.
865 void cancel_reconnect_delay();
867 /// \brief Change address of server for this session.
868 void override_server(std::string address, port_type);
872 Impl* m_impl = nullptr;
874 void abandon() noexcept;
875 void async_wait_for(bool upload_completion, bool download_completion,
876 WaitOperCompletionHandler);
880 /// \brief Protocol errors discovered by the client.
882 /// These errors will terminate the network connection (disconnect all sessions
883 /// associated with the affected connection), and the error will be reported to
884 /// the application via the connection state change listeners of the affected
886 enum class Client::Error {
887 connection_closed = 100, ///< Connection closed (no error)
888 unknown_message = 101, ///< Unknown type of input message
889 bad_syntax = 102, ///< Bad syntax in input message head
890 limits_exceeded = 103, ///< Limits exceeded in input message
891 bad_session_ident = 104, ///< Bad session identifier in input message
892 bad_message_order = 105, ///< Bad input message order
893 bad_file_ident_pair = 106, ///< Bad file identifier pair (ALLOC)
894 bad_progress = 107, ///< Bad progress information (DOWNLOAD)
895 bad_changeset_header_syntax = 108, ///< Bad syntax in changeset header (DOWNLOAD)
896 bad_changeset_size = 109, ///< Bad changeset size in changeset header (DOWNLOAD)
897 bad_origin_file_ident = 110, ///< Bad origin file identifier in changeset header (DOWNLOAD)
898 bad_server_version = 111, ///< Bad server version in changeset header (DOWNLOAD)
899 bad_changeset = 112, ///< Bad changeset (DOWNLOAD)
900 bad_request_ident = 113, ///< Bad request identifier (MARK)
901 bad_error_code = 114, ///< Bad error code (ERROR),
902 bad_compression = 115, ///< Bad compression (DOWNLOAD)
903 bad_client_version = 116, ///< Bad last integrated client version in changeset header (DOWNLOAD)
904 ssl_server_cert_rejected = 117, ///< SSL server certificate rejected
905 pong_timeout = 118, ///< Timeout on reception of PONG respone message
908 const std::error_category& client_error_category() noexcept;
910 std::error_code make_error_code(Client::Error) noexcept;
917 template<> struct is_error_code_enum<realm::sync::Client::Error> {
918 static const bool value = true;
930 class BadServerUrl: public std::exception {
932 const char* what() const noexcept override
934 return "Bad server URL";
938 inline Session::Session(Session&& sess) noexcept:
941 sess.m_impl = nullptr;
944 inline Session::Session() noexcept
948 inline Session::~Session() noexcept
954 inline Session& Session::operator=(Session&& sess) noexcept
958 m_impl = sess.m_impl;
959 sess.m_impl = nullptr;
963 inline void Session::detach() noexcept
970 inline void Session::set_error_handler(std::function<ErrorHandler> handler)
972 auto handler_2 = [handler=std::move(handler)](ConnectionState state,
973 const ErrorInfo* error_info) {
974 if (state != ConnectionState::disconnected)
976 REALM_ASSERT(error_info);
977 std::error_code ec = error_info->error_code;
978 bool is_fatal = error_info->is_fatal;
979 const std::string& detailed_message = error_info->detailed_message;
980 handler(ec, is_fatal, detailed_message); // Throws
982 set_connection_state_change_listener(std::move(handler_2)); // Throws
985 inline void Session::async_wait_for_sync_completion(WaitOperCompletionHandler handler)
987 bool upload_completion = true, download_completion = true;
988 async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
991 inline void Session::async_wait_for_upload_completion(WaitOperCompletionHandler handler)
993 bool upload_completion = true, download_completion = false;
994 async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
997 inline void Session::async_wait_for_download_completion(WaitOperCompletionHandler handler)
999 bool upload_completion = false, download_completion = true;
1000 async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
1004 } // namespace realm
1006 #endif // REALM_SYNC_CLIENT_HPP