added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / sync / client.hpp
diff --git a/iOS/Pods/Realm/include/core/realm/sync/client.hpp b/iOS/Pods/Realm/include/core/realm/sync/client.hpp
new file mode 100644 (file)
index 0000000..c08ff96
--- /dev/null
@@ -0,0 +1,1006 @@
+/*************************************************************************
+ *
+ * REALM CONFIDENTIAL
+ * __________________
+ *
+ *  [2011] - [2012] Realm Inc
+ *  All Rights Reserved.
+ *
+ * NOTICE:  All information contained herein is, and remains
+ * the property of Realm Incorporated and its suppliers,
+ * if any.  The intellectual and technical concepts contained
+ * herein are proprietary to Realm Incorporated
+ * and its suppliers and may be covered by U.S. and Foreign Patents,
+ * patents in process, and are protected by trade secret or copyright law.
+ * Dissemination of this information or reproduction of this material
+ * is strictly forbidden unless prior written permission is obtained
+ * from Realm Incorporated.
+ *
+ **************************************************************************/
+#ifndef REALM_SYNC_CLIENT_HPP
+#define REALM_SYNC_CLIENT_HPP
+
+#include <stdint.h>
+#include <memory>
+#include <utility>
+#include <functional>
+#include <exception>
+#include <string>
+
+#include <realm/util/logger.hpp>
+#include <realm/util/network.hpp>
+#include <realm/impl/cont_transact_hist.hpp>
+#include <realm/sync/history.hpp>
+
+namespace realm {
+namespace sync {
+
+class Client {
+public:
+    enum class Error;
+
+    enum class ReconnectMode {
+        /// This is the mode that should always be used in production. In this
+        /// mode the client uses a scheme for determining a reconnect delay that
+        /// prevents it from creating too many connection requests in a short
+        /// amount of time (i.e., a server hammering protection mechanism).
+        normal,
+
+        /// Delay reconnect attempts indefinitely. For testing purposes only.
+        ///
+        /// A reconnect attempt can be manually scheduled by calling
+        /// cancel_reconnect_delay(). In particular, when a connection breaks,
+        /// or when an attempt at establishing the connection fails, the
+        /// connection state change listener is called. If one calls
+        /// cancel_reconnect_delay() from that invocation of the listener, the
+        /// effect is to allow another reconnect attempt to occur.
+        never,
+
+        /// Never delay reconnect attempts. Perform them immediately. For
+        /// testing purposes only.
+        immediate
+    };
+
+    static constexpr std::uint_fast64_t default_ping_keepalive_period_ms  = 600000; // 10 minutes
+    static constexpr std::uint_fast64_t default_pong_keepalive_timeout_ms = 300000; //  5 minutes
+    static constexpr std::uint_fast64_t default_pong_urgent_timeout_ms    =   5000; //  5 seconds
+
+    struct Config {
+        Config() {}
+
+        /// The maximum number of Realm files that will be kept open
+        /// concurrently by this client. The client keeps a cache of open Realm
+        /// files for efficiency reasons.
+        long max_open_files = 256;
+
+        /// An optional logger to be used by the client. If no logger is
+        /// specified, the client will use an instance of util::StderrLogger
+        /// with the log level threshold set to util::Logger::Level::info. The
+        /// client does not require a thread-safe logger, and it guarantees that
+        /// all logging happens either on behalf of the constructor or on behalf
+        /// of the invocation of run().
+        util::Logger* logger = nullptr;
+
+        /// Use ports 80 and 443 by default instead of 7800 and 7801
+        /// respectively. Ideally, these default ports should have been made
+        /// available via a different URI scheme instead (http/https or ws/wss).
+        bool enable_default_port_hack = true;
+
+        /// For testing purposes only.
+        ReconnectMode reconnect_mode = ReconnectMode::normal;
+
+        /// Create a separate connection for each session. For testing purposes
+        /// only.
+        ///
+        /// FIXME: This setting needs to be true for now, due to limitations in
+        /// the load balancer.
+        bool one_connection_per_session = true;
+
+        /// Do not access the local file system. Sessions will act as if
+        /// initiated on behalf of an empty (or nonexisting) local Realm
+        /// file. Received DOWNLOAD messages will be accepted, but otherwise
+        /// ignored. No UPLOAD messages will be generated. For testing purposes
+        /// only.
+        bool dry_run = false;
+
+        /// The default changeset cooker to be used by new sessions. Can be
+        /// overridden by Session::Config::changeset_cooker.
+        ///
+        /// \sa make_client_history(), TrivialChangesetCooker.
+        std::shared_ptr<ClientHistory::ChangesetCooker> changeset_cooker;
+
+        /// The number of ms between periodic keep-alive pings.
+        std::uint_fast64_t ping_keepalive_period_ms = default_ping_keepalive_period_ms;
+
+        /// The number of ms to wait for keep-alive pongs.
+        std::uint_fast64_t pong_keepalive_timeout_ms = default_pong_keepalive_timeout_ms;
+
+        /// The number of ms to wait for urgent pongs.
+        std::uint_fast64_t pong_urgent_timeout_ms = default_pong_urgent_timeout_ms;
+
+        /// If enable_upload_log_compaction is true, every changeset will be
+        /// compacted before it is uploaded to the server. Compaction will
+        /// reduce the size of a changeset if the same field is set multiple
+        /// times or if newly created objects are deleted within the same
+        /// transaction. Log compaction increeses CPU usage and memory
+        /// consumption.
+        bool enable_upload_log_compaction = true;
+
+        /// Set the `TCP_NODELAY` option on all TCP/IP sockets. This disables
+        /// the Nagle algorithm. Disabling it, can in some cases be used to
+        /// decrease latencies, but possibly at the expense of scalability. Be
+        /// sure to research the subject before you enable this option.
+        bool tcp_no_delay = false;
+    };
+
+    /// \throw util::EventLoop::Implementation::NotAvailable if no event loop
+    /// implementation was specified, and
+    /// util::EventLoop::Implementation::get_default() throws it.
+    Client(Config = {});
+    Client(Client&&) noexcept;
+    ~Client() noexcept;
+
+    /// Run the internal event-loop of the client. At most one thread may
+    /// execute run() at any given time. The call will not return until somebody
+    /// calls stop().
+    void run();
+
+    /// See run().
+    ///
+    /// Thread-safe.
+    void stop() noexcept;
+
+    /// \brief Cancel current or next reconnect delay for all servers.
+    ///
+    /// This corresponds to calling Session::cancel_reconnect_delay() on all
+    /// bound sessions, but will also cancel reconnect delays applying to
+    /// servers for which there are currently no bound sessions.
+    ///
+    /// Thread-safe.
+    void cancel_reconnect_delay();
+
+    /// \brief Wait for session termination to complete.
+    ///
+    /// Wait for termination of all sessions whose termination was initiated
+    /// prior this call (the completion condition), or until the client's event
+    /// loop thread exits from Client::run(), whichever happens
+    /// first. Termination of a session can be initiated implicitly (e.g., via
+    /// destruction of the session object), or explicitly by Session::detach().
+    ///
+    /// Note: After session termination (when this function returns true) no
+    /// session specific callback function can be called or continue to execute,
+    /// and the client is guaranteed to no longer have a Realm file open on
+    /// behalf of the terminated session.
+    ///
+    /// CAUTION: If run() returns while a wait operation is in progress, this
+    /// waiting function will return immediately, even if the completion
+    /// condition is not yet satisfied. The completion condition is guaranteed
+    /// to be satisfied only when these functions return true. If it returns
+    /// false, session specific callback functions may still be executing or get
+    /// called, and the associated Realm files may still not have been closed.
+    ///
+    /// If a new wait operation is initiated while another wait operation is in
+    /// progress by another thread, the waiting period of fist operation may, or
+    /// may not get extended. The application must not assume either.
+    ///
+    /// Note: Session termination does not imply that the client has received an
+    /// UNBOUND message from the server (see the protocol specification). This
+    /// may happen later.
+    ///
+    /// \return True only if the completion condition was satisfied. False if
+    /// the client's event loop thread exited from Client::run() in which case
+    /// the completion condition may, or may not have been satisfied.
+    ///
+    /// Note: These functions are fully thread-safe. That is, they may be called
+    /// by any thread, and by multiple threads concurrently.
+    bool wait_for_session_terminations_or_client_stopped();
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> m_impl;
+    friend class Session;
+};
+
+
+/// Supported protocols:
+///
+///      Protocol    URL scheme     Default port
+///     -----------------------------------------------------------------------------------
+///      realm       "realm:"       7800 (80 if Client::Config::enable_default_port_hack)
+///      realm_ssl   "realms:"      7801 (443 if Client::Config::enable_default_port_hack)
+///
+enum class Protocol {
+    realm,
+    realm_ssl
+};
+
+
+class BadServerUrl; // Exception
+
+
+/// \brief Client-side representation of a Realm file synchronization session.
+///
+/// A synchronization session deals with precisely one local Realm file. To
+/// synchronize multiple local Realm files, you need multiple sessions.
+///
+/// A session object is always associated with a particular client object (\ref
+/// Client). The application must ensure that the destruction of the associated
+/// client object never happens before the destruction of the session
+/// object. The consequences of a violation are unspecified.
+///
+/// A session object is always associated with a particular local Realm file,
+/// however, a session object does not represent a session until it is bound to
+/// a server side Realm, i.e., until bind() is called. From the point of view of
+/// the thread that calls bind(), the session starts precisely when the
+/// execution of bind() starts, i.e., before bind() returns.
+///
+/// At most one session is allowed to exist for a particular local Realm file
+/// (file system inode) at any point in time. Multiple session objects may
+/// coexists for a single file, as long as bind() has been called on at most one
+/// of them. Additionally, two bound session objects for the same file are
+/// allowed to exist at different times, if they have no overlap in time (in
+/// their bound state), as long as they are associated with the same client
+/// object, or with two different client objects that do not overlap in
+/// time. This means, in particular, that it is an error to create two bound
+/// session objects for the same local Realm file, it they are associated with
+/// two different client objects that overlap in time, even if the session
+/// objects do not overlap in time (in their bound state). It is the
+/// responsibility of the application to ensure that these rules are adhered
+/// to. The consequences of a violation are unspecified.
+///
+/// Thread-safety: It is safe for multiple threads to construct, use (with some
+/// exceptions), and destroy session objects concurrently, regardless of whether
+/// those session objects are associated with the same, or with different Client
+/// objects. Please note that some of the public member functions are fully
+/// thread-safe, while others are not.
+///
+/// Callback semantics: All session specific callback functions will be executed
+/// by the event loop thread, i.e., the thread that calls Client::run(). No
+/// callback function will be called before Session::bind() is called. Callback
+/// functions that are specified prior to calling bind() (e.g., any passed to
+/// set_progress_handler()) may start to execute before bind() returns, as long
+/// as some thread is executing Client::run(). Likewise, completion handlers,
+/// such as those passed to async_wait_for_sync_completion() may start to
+/// execute before the submitting function returns. All session specific
+/// callback functions (including completion handlers) are guaranteed to no
+/// longer be executing when session termination completes, and they are
+/// guaranteed to not be called after session termination completes. Termination
+/// is an event that completes asynchronously with respect to the application,
+/// but is initiated by calling detach(), or implicitely by destroying a session
+/// object. After having initiated one or more session terminations, the
+/// application can wait for those terminations to complete by calling
+/// Client::wait_for_session_terminations_or_client_stopped(). Since callback
+/// functinos are always executed by the event loop thread, they are also
+/// guaranteed to not be executing after Client::run() has returned.
+class Session {
+public:
+    using port_type = util::network::Endpoint::port_type;
+    using version_type = _impl::History::version_type;
+    using SyncTransactCallback = void(VersionID old_version, VersionID new_version);
+    using ProgressHandler = void(std::uint_fast64_t downloaded_bytes,
+                                 std::uint_fast64_t downloadable_bytes,
+                                 std::uint_fast64_t uploaded_bytes,
+                                 std::uint_fast64_t uploadable_bytes,
+                                 std::uint_fast64_t progress_version,
+                                 std::uint_fast64_t snapshot_version);
+    using WaitOperCompletionHandler = std::function<void(std::error_code)>;
+    using SSLVerifyCallback = bool(const std::string& server_address,
+                                   port_type server_port,
+                                   const char* pem_data,
+                                   size_t pem_size,
+                                   int preverify_ok,
+                                   int depth);
+
+    class Config {
+    public:
+        Config() {}
+
+        /// server_address is the fully qualified host name, or IP address of
+        /// the server.
+        std::string server_address = "localhost";
+
+        /// server_port is the port at which the server listens. If server_port
+        /// is zero, the default port for the specified protocol is used. See \ref
+        /// Protocol for information on default ports.
+        port_type server_port = 0;
+
+        /// server_path is  the virtual path by which the server identifies the
+        /// Realm. This path must always be an absolute path, and must therefore
+        /// always contain a leading slash (`/`). Further more, each segment of the
+        /// virtual path must consist of one or more characters that are either
+        /// alpha-numeric or in (`_`, `-`, `.`), and each segment is not allowed to
+        /// equal `.` or `..`, and must not end with `.realm`, `.realm.lock`, or
+        /// `.realm.management`. These rules are necessary because the server
+        /// currently reserves the right to use the specified path as part of the
+        /// file system path of a Realm file. It is expected that these rules will
+        /// be significantly relaxed in the future by completely decoupling the
+        /// virtual paths from actual file system paths.
+        std::string server_path = "/";
+
+        /// The protocol used for communicating with the server. See \ref Protocol.
+        Protocol protocol = Protocol::realm;
+
+        /// url_prefix is a prefix that is prepended to the server_path
+        /// in the HTTP GET request that initiates a sync connection. The value
+        /// specified here must match with the server's expectation. Changing
+        /// the value of url_prefix should be matched with a corresponding
+        /// change of the server side proxy.
+        std::string url_prefix = "/realm-sync";
+
+        /// Sessions can be multiplexed over the same TCP/SSL connection.
+        /// Sessions might share connection if they have identical server_address,
+        /// server_port, and protocol. multiplex_ident is a parameter that allows
+        /// finer control over session multiplexing. If two sessions have distinct
+        /// multiplex_ident, they will never share connection. The typical use of
+        /// multilex_ident is to give sessions with incompatible SSL requirements
+        /// distinct multiplex_idents.
+        /// multiplex_ident can be any string and the value has no meaning except
+        /// for partitioning the sessions.
+        std::string multiplex_ident;
+
+        /// verify_servers_ssl_certificate controls whether the server
+        /// certificate is verified for SSL connections. It should generally be
+        /// true in production.
+        bool verify_servers_ssl_certificate = true;
+
+        /// ssl_trust_certificate_path is the path of a trust/anchor
+        /// certificate used by the client to verify the server certificate.
+        /// ssl_trust_certificate_path is only used if the protocol is ssl and
+        /// verify_servers_ssl_certificate is true.
+        ///
+        /// A server certificate is verified by first checking that the
+        /// certificate has a valid signature chain back to a trust/anchor
+        /// certificate, and secondly checking that the server_address matches
+        /// a host name contained in the certificate. The host name of the
+        /// certificate is stored in either Common Name or the Alternative
+        /// Subject Name (DNS section).
+        ///
+        /// If ssl_trust_certificate_path is None (default), ssl_verify_callback
+        /// (see below) is used if set, and the default device trust/anchor
+        /// store is used otherwise.
+        Optional<std::string> ssl_trust_certificate_path;
+
+        /// If Client::Config::ssl_verify_callback is set, that function is called
+        /// to verify the certificate, unless verify_servers_ssl_certificate is
+        /// false.
+
+        /// ssl_verify_callback is used to implement custom SSL certificate
+        /// verification. it is only used if the protocol is SSL,
+        /// verify_servers_ssl_certificate is true and ssl_trust_certificate_path
+        /// is None.
+        ///
+        /// The signature of ssl_verify_callback is
+        ///
+        /// bool(const std::string& server_address,
+        ///      port_type server_port,
+        ///      const char* pem_data,
+        ///      size_t pem_size,
+        ///      int preverify_ok,
+        ///      int depth);
+        ///
+        /// server address and server_port is the address and port of the server
+        /// that a SSL connection is being established to. They are identical to
+        /// the server_address and server_port set in this config file and are
+        /// passed for convenience.
+        /// pem_data is the certificate of length pem_size in
+        /// the PEM format. preverify_ok is OpenSSL's preverification of the
+        /// certificate. preverify_ok is either 0, or 1. If preverify_ok is 1,
+        /// OpenSSL has accepted the certificate and it will generally be safe
+        /// to trust that certificate. depth represents the position of the
+        /// certificate in the certificate chain sent by the server. depth = 0
+        /// represents the actual server certificate that should contain the
+        /// host name(server address) of the server. The highest depth is the
+        /// root certificate.
+        /// The callback function will receive the certificates starting from
+        /// the root certificate and moving down the chain until it reaches the
+        /// server's own certificate with a host name. The depth of the last
+        /// certificate is 0. The depth of the first certificate is chain
+        /// length - 1.
+        ///
+        /// The return value of the callback function decides whether the
+        /// client accepts the certificate. If the return value is false, the
+        /// processing of the certificate chain is interrupted and the SSL
+        /// connection is rejected. If the return value is true, the verification
+        /// process continues. If the callback function returns true for all
+        /// presented certificates including the depth == 0 certificate, the
+        /// SSL connection is accepted.
+        ///
+        /// A recommended way of using the callback function is to return true
+        /// if preverify_ok = 1 and depth > 0,
+        /// always check the host name if depth = 0,
+        /// and use an independent verification step if preverify_ok = 0.
+        ///
+        /// Another possible way of using the callback is to collect all the
+        /// certificates until depth = 0, and present the entire chain for
+        /// independent verification.
+        std::function<SSLVerifyCallback> ssl_verify_callback;
+
+        /// signed_user_token is a cryptographically signed token describing the
+        /// identity and access rights of the current user.
+        std::string signed_user_token;
+
+        /// If not null, overrides whatever is specified by
+        /// Client::Config::changeset_cooker.
+        ///
+        /// The shared ownership over the cooker will be relinquished shortly
+        /// after the destruction of the session object as long as the event
+        /// loop of the client is being executed (Client::run()).
+        ///
+        /// CAUTION: ChangesetCooker::cook_changeset() of the specified cooker
+        /// may get called before the call to bind() returns, and it may get
+        /// called (or continue to execute) after the session object is
+        /// destroyed. Please see "Callback semantics" section under Client for
+        /// more on this.
+        ///
+        /// \sa make_client_history(), TrivialChangesetCooker.
+        std::shared_ptr<ClientHistory::ChangesetCooker> changeset_cooker;
+
+        /// The encryption key the SharedGroup will be opened with.
+        Optional<std::array<char, 64>> encryption_key;
+
+        /// FIXME: This value must currently be true in a cluster setup.
+        /// This restriction will be lifted in the future.
+        bool one_connection_per_session = true;
+    };
+
+    /// \brief Start a new session for the specified client-side Realm.
+    ///
+    /// Note that the session is not fully activated until you call bind(). Also
+    /// note that if you call set_sync_transact_callback(), it must be done
+    /// before calling bind().
+    ///
+    /// \param realm_path The file-system path of a local client-side Realm
+    /// file.
+    Session(Client&, std::string realm_path, Config = {});
+
+    /// This leaves the right-hand side session object detached. See "Thread
+    /// safety" section under detach().
+    Session(Session&&) noexcept;
+
+    /// Create a detached session object (see detach()).
+    Session() noexcept;
+
+    /// Implies detachment. See "Thread safety" section under detach().
+    ~Session() noexcept;
+
+    /// Detach the object on the left-hand side, then "steal" the session from
+    /// the object on the right-hand side, if there is one. This leaves the
+    /// object on the right-hand side detached. See "Thread safety" section
+    /// under detach().
+    Session& operator=(Session&&) noexcept;
+
+    /// Detach this sesion object from the client object (Client). If the
+    /// session object is already detached, this function has no effect
+    /// (idempotency).
+    ///
+    /// Detachment initiates session termination, which is an event that takes
+    /// place shortly therafter in the context of the client's event loop
+    /// thread.
+    ///
+    /// A detached session object may be destroyed, move-assigned to, and moved
+    /// from. Apart from that, it is an error to call any function other than
+    /// detach() on a detached session object.
+    ///
+    /// Thread safety: Detachment is not a thread-safe operation. This means
+    /// that detach() may not be executed by two threads concurrently, and may
+    /// not execute concurrently with object destruction. Additionally,
+    /// detachment must not execute concurrently with a moving operation
+    /// involving the session object on the left or right-hand side. See move
+    /// constructor and assigment operator.
+    void detach() noexcept;
+
+    /// \brief Set a function to be called when the local Realm has changed due
+    /// to integration of a downloaded changeset.
+    ///
+    /// Specify the callback function that will be called when one or more
+    /// transactions are performed to integrate downloaded changesets into the
+    /// client-side Realm, that is associated with this session.
+    ///
+    /// The callback function will always be called by the thread that executes
+    /// the event loop (Client::run()), but not until bind() is called. If the
+    /// callback function throws an exception, that exception will "travel" out
+    /// through Client::run().
+    ///
+    /// Note: Any call to this function must have returned before bind() is
+    /// called. If this function is called multiple times, each call overrides
+    /// the previous setting.
+    ///
+    /// Note: This function is **not thread-safe**. That is, it is an error if
+    /// it is called while another thread is executing any member function on
+    /// the same Session object.
+    ///
+    /// CAUTION: The specified callback function may get called before the call
+    /// to bind() returns, and it may get called (or continue to execute) after
+    /// the session object is destroyed. Please see "Callback semantics" section
+    /// under Session for more on this.
+    void set_sync_transact_callback(std::function<SyncTransactCallback>);
+
+    /// \brief Set a handler to monitor the state of download and upload
+    /// progress.
+    ///
+    /// The handler must have signature
+    ///
+    ///     void(uint_fast64_t downloaded_bytes, uint_fast64_t downloadable_bytes,
+    ///          uint_fast64_t uploaded_bytes, uint_fast64_t uploadable_bytes,
+    ///          uint_fast64_t progress_version);
+    ///
+    /// downloaded_bytes is the size in bytes of all downloaded changesets.
+    /// downloadable_bytes is the size in bytes of the part of the server
+    /// history that do not originate from this client.
+    ///
+    /// uploaded_bytes is the size in bytes of all locally produced changesets
+    /// that have been received and acknowledged by the server.
+    /// uploadable_bytes is the size in bytes of all locally produced changesets.
+    ///
+    /// Due to the nature of the merge rules, it is possible that the size of an
+    /// uploaded changeset uploaded from one client is not equal to the size of
+    /// the changesets that other clients will download.
+    ///
+    /// Typical uses of this function:
+    ///
+    /// Upload completion can be checked by
+    ///
+    ///    bool upload_complete = (uploaded_bytes == uploadable_bytes);
+    ///
+    /// Download completion could be checked by
+    ///
+    ///     bool download_complete = (downloaded_bytes == downloadable_bytes);
+    ///
+    /// However, download completion might never be reached because the server
+    /// can receive new changesets from other clients.
+    /// An alternative strategy is to cache downloadable_bytes from the callback,
+    /// and use the cached value as the threshold.
+    ///
+    ///     bool download_complete = (downloaded_bytes == cached_downloadable_bytes);
+    ///
+    /// Upload progress can be calculated by caching an initial value of
+    /// uploaded_bytes from the last, or next, callback. Then
+    ///
+    ///     double upload_progress =
+    ///        (uploaded_bytes - initial_uploaded_bytes)
+    ///       -------------------------------------------
+    ///       (uploadable_bytes - initial_uploaded_bytes)
+    ///
+    /// Download progress can be calculates similarly:
+    ///
+    ///     double download_progress =
+    ///        (downloaded_bytes - initial_downloaded_bytes)
+    ///       -----------------------------------------------
+    ///       (downloadable_bytes - initial_downloaded_bytes)
+    ///
+    /// progress_version is 0 at the start of a session. When at least one
+    /// DOWNLOAD message has been received from the server, progress_version is
+    /// positive. progress_version can be used to ensure that the reported
+    /// progress contains information obtained from the server in the current
+    /// session. The server will send a message as soon as possible, and the
+    /// progress handler will eventually be called with a positive progress_version
+    /// unless the session is interrupted before a message from the server has
+    /// been received.
+    ///
+    /// The handler is called on the event loop thread.The handler after bind(),
+    /// after each DOWNLOAD message, and after each local transaction
+    /// (nonsync_transact_notify).
+    ///
+    /// set_progress_handler() is not thread safe and it must be called before
+    /// bind() is called. Subsequent calls to set_progress_handler() overwrite
+    /// the previous calls. Typically, this function is called once per session.
+    ///
+    /// CAUTION: The specified callback function may get called before the call
+    /// to bind() returns, and it may get called (or continue to execute) after
+    /// the session object is destroyed. Please see "Callback semantics" section
+    /// under Session for more on this.
+    void set_progress_handler(std::function<ProgressHandler>);
+
+    enum class ConnectionState { disconnected, connecting, connected };
+
+    /// \brief Information about an error causing a session to be temporarily
+    /// disconnected from the server.
+    ///
+    /// In general, the connection will be automatically reestablished
+    /// later. Whether this happens quickly, generally depends on \ref
+    /// is_fatal. If \ref is_fatal is true, it means that the error is deemed to
+    /// be of a kind that is likely to persist, and cause all future reconnect
+    /// attempts to fail. In that case, if another attempt is made at
+    /// reconnecting, the delay will be substantial (at least an hour).
+    ///
+    /// \ref error_code specifies the error that caused the connection to be
+    /// closed. For the list of errors reported by the server, see \ref
+    /// ProtocolError (or `protocol.md`). For the list of errors corresponding
+    /// to protocol violations that are detected by the client, see
+    /// Client::Error. The error may also be a system level error, or an error
+    /// from one of the potential intermediate protocol layers (SSL or
+    /// WebSocket).
+    ///
+    /// \ref detailed_message is the most detailed message available to describe
+    /// the error. It is generally equal to `error_code.message()`, but may also
+    /// be a more specific message (one that provides extra context). The
+    /// purpose of this message is mostly to aid in debugging. For non-debugging
+    /// purposes, `error_code.message()` should generally be considered
+    /// sufficient.
+    ///
+    /// \sa set_connection_state_change_listener().
+    struct ErrorInfo {
+        std::error_code error_code;
+        bool is_fatal;
+        const std::string& detailed_message;
+    };
+
+    using ConnectionStateChangeListener = void(ConnectionState, const ErrorInfo*);
+
+    /// \brief Install a connection state change listener.
+    ///
+    /// Sets a function to be called whenever the state of the underlying
+    /// network connection changes between "disconnected", "connecting", and
+    /// "connected". The initial state is always "disconnected". The next state
+    /// after "disconnected" is always "connecting". The next state after
+    /// "connecting" is either "connected" or "disconnected". The next state
+    /// after "connected" is always "disconnected". A switch to the
+    /// "disconnected" state only happens when an error occurs.
+    ///
+    /// Whenever the installed function is called, an ErrorInfo object is passed
+    /// when, and only when the passed state is ConnectionState::disconnected.
+    ///
+    /// When multiple sessions share a single connection, the state changes will
+    /// be reported for each session in turn.
+    ///
+    /// The callback function will always be called by the thread that executes
+    /// the event loop (Client::run()), but not until bind() is called. If the
+    /// callback function throws an exception, that exception will "travel" out
+    /// through Client::run().
+    ///
+    /// Note: Any call to this function must have returned before bind() is
+    /// called. If this function is called multiple times, each call overrides
+    /// the previous setting.
+    ///
+    /// Note: This function is **not thread-safe**. That is, it is an error if
+    /// it is called while another thread is executing any member function on
+    /// the same Session object.
+    ///
+    /// CAUTION: The specified callback function may get called before the call
+    /// to bind() returns, and it may get called (or continue to execute) after
+    /// the session object is destroyed. Please see "Callback semantics" section
+    /// under Session for more on this.
+    void set_connection_state_change_listener(std::function<ConnectionStateChangeListener>);
+
+    //@{
+    /// Deprecated! Use set_connection_state_change_listener() instead.
+    using ErrorHandler = void(std::error_code, bool is_fatal, const std::string& detailed_message);
+    void set_error_handler(std::function<ErrorHandler>);
+    //@}
+
+    /// @{ \brief Bind this session to the specified server side Realm.
+    ///
+    /// No communication takes place on behalf of this session before the
+    /// session is bound, but as soon as the session becomes bound, the server
+    /// will start to push changes to the client, and vice versa.
+    ///
+    /// If a callback function was set using set_sync_transact_callback(), then
+    /// that callback function will start to be called as changesets are
+    /// downloaded and integrated locally. It is important to understand that
+    /// callback functions are executed by the event loop thread (Client::run())
+    /// and the callback function may therefore be called before bind() returns.
+    ///
+    /// Note: It is an error if this function is called more than once per
+    /// Session object.
+    ///
+    /// Note: This function is **not thread-safe**. That is, it is an error if
+    /// it is called while another thread is executing any member function on
+    /// the same Session object.
+    ///
+    /// bind() binds this session to the specified server side Realm using the
+    /// parameters specified in the Session::Config object.
+    ///
+    /// The two other forms of bind() are convenience functions.
+    /// void bind(std::string server_address, std::string server_path,
+    ///           std::string signed_user_token, port_type server_port = 0,
+    ///           Protocol protocol = Protocol::realm);
+    /// replaces the corresponding parameters from the Session::Config object
+    /// before the session is bound.
+    /// void bind(std::string server_url, std::string signed_user_token) parses
+    /// the \param server_url and replaces the parameters in the Session::Config object
+    /// before the session is bound.
+    ///
+    /// \param server_url For example "realm://sync.realm.io/test". See
+    /// server_address, server_path, and server_port in Session::Config for information
+    /// about the individual components of the URL. See \ref Protocol for the list of
+    /// available URL schemes and the associated default ports.
+    ///
+    /// \throw BadServerUrl if the specified server URL is malformed.
+    void bind();
+    void bind(std::string server_url, std::string signed_user_token);
+    void bind(std::string server_address, std::string server_path,
+              std::string signed_user_token, port_type server_port = 0,
+              Protocol protocol = Protocol::realm);
+    /// @}
+
+    /// \brief Refresh the user token associated with this session.
+    ///
+    /// This causes the REFRESH protocol message to be sent to the server. See
+    /// \ref Protocol.
+    ///
+    /// In an on-going session a client may expect its access token to expire at
+    /// a certain time and schedule acquisition of a fresh access token (using a
+    /// refresh token or by other means) in due time to provide a better user
+    /// experience. Without refreshing the token, the client will be notified
+    /// that the session is terminated due to insufficient privileges and must
+    /// reacquire a fresh token, which is a potentially disruptive process.
+    ///
+    /// It is an error to call this function before calling `Client::bind()`.
+    ///
+    /// Note: This function is thread-safe.
+    ///
+    /// \param signed_user_token A cryptographically signed token describing the
+    /// identity and access rights of the current user. See \ref Protocol.
+    void refresh(std::string signed_user_token);
+
+    /// \brief Inform the synchronization agent about changes of local origin.
+    ///
+    /// This function must be called by the application after a transaction
+    /// performed on its behalf, that is, after a transaction that is not
+    /// performed to integrate a changeset that was downloaded from the server.
+    ///
+    /// It is an error to call this function before bind() has been called, and
+    /// has returned.
+    ///
+    /// Note: This function is fully thread-safe. That is, it may be called by
+    /// any thread, and by multiple threads concurrently.
+    void nonsync_transact_notify(version_type new_version);
+
+    /// @{ \brief Wait for upload, download, or upload+download completion.
+    ///
+    /// async_wait_for_upload_completion() initiates an asynchronous wait for
+    /// upload to complete, async_wait_for_download_completion() initiates an
+    /// asynchronous wait for download to complete, and
+    /// async_wait_for_sync_completion() initiates an asynchronous wait for
+    /// upload and download to complete.
+    ///
+    /// Upload is considered complete when all non-empty changesets of local
+    /// origin have been uploaded to the server, and the server has acknowledged
+    /// reception of them. Changesets of local origin introduced after the
+    /// initiation of the session (after bind() is called) will generally not be
+    /// considered for upload unless they are announced to this client through
+    /// nonsync_transact_notify() prior to the initiation of the wait operation,
+    /// i.e., prior to the invocation of async_wait_for_upload_completion() or
+    /// async_wait_for_sync_completion(). Unannounced changesets may get picked
+    /// up, but there is no guarantee that they will be, however, if a certain
+    /// changeset is announced, then all previous changesets are implicitly
+    /// announced. Also all preexisting changesets are implicitly announced
+    /// when the session is initiated.
+    ///
+    /// Download is considered complete when all non-empty changesets of remote
+    /// origin have been downloaded from the server, and integrated into the
+    /// local Realm state. To know what is currently outstanding on the server,
+    /// the client always sends a special "marker" message to the server, and
+    /// waits until it has downloaded all outstanding changesets that were
+    /// present on the server at the time when the server received that marker
+    /// message. Each call to async_wait_for_download_completion() and
+    /// async_wait_for_sync_completion() therefore requires a full client <->
+    /// server round-trip.
+    ///
+    /// If a new wait operation is initiated while another wait operation is in
+    /// progress by another thread, the waiting period of first operation may,
+    /// or may not get extended. The application must not assume either. The
+    /// application may assume, however, that async_wait_for_upload_completion()
+    /// will not affect the waiting period of
+    /// async_wait_for_download_completion(), and vice versa.
+    ///
+    /// It is an error to call these functions before bind() has been called,
+    /// and has returned.
+    ///
+    /// The specified completion handlers will always be executed by the thread
+    /// that executes the event loop (the thread that calls Client::run()). If
+    /// the handler throws an exception, that exception will "travel" out
+    /// through Client::run().
+    ///
+    /// If incomplete wait operations exist when the session is terminated,
+    /// those wait operations will be canceled. Session termination is an event
+    /// that happens in the context of the client's event loop thread shortly
+    /// after the destruction of the session object. The std::error_code
+    /// argument passed to the completion handler of a canceled wait operation
+    /// will be `util::error::operation_aborted`. For uncanceled wait operations
+    /// it will be `std::error_code{}`. Note that as long as the client's event
+    /// loop thread is running, all completion handlers will be called
+    /// regardless of whether the operations get canceled or not.
+    ///
+    /// CAUTION: The specified completion handlers may get called before the
+    /// call to the waiting function returns, and it may get called (or continue
+    /// to execute) after the session object is destroyed. Please see "Callback
+    /// semantics" section under Session for more on this.
+    ///
+    /// Note: These functions are fully thread-safe. That is, they may be called
+    /// by any thread, and by multiple threads concurrently.
+    void async_wait_for_sync_completion(WaitOperCompletionHandler);
+    void async_wait_for_upload_completion(WaitOperCompletionHandler);
+    void async_wait_for_download_completion(WaitOperCompletionHandler);
+    /// @}
+
+    /// @{ \brief Synchronous wait for upload or download completion.
+    ///
+    /// These functions are synchronous equivalents to
+    /// async_wait_for_upload_completion() and
+    /// async_wait_for_download_completion() respectively. This means that they
+    /// block the caller until the completion condition is satisfied, or the
+    /// client's event loop thread exits from Client::run(), whichever happens
+    /// first.
+    ///
+    /// It is an error to call these functions before bind() has been called,
+    /// and has returned.
+    ///
+    /// CAUTION: If Client::run() returns while a wait operation is in progress,
+    /// these waiting functions return immediately, even if the completion
+    /// condition is not yet satisfied. The completion condition is guaranteed
+    /// to be satisfied only when these functions return true.
+    ///
+    /// \return True only if the completion condition was satisfied. False if
+    /// the client's event loop thread exited from Client::run() in which case
+    /// the completion condition may, or may not have been satisfied.
+    ///
+    /// Note: These functions are fully thread-safe. That is, they may be called
+    /// by any thread, and by multiple threads concurrently.
+    bool wait_for_upload_complete_or_client_stopped();
+    bool wait_for_download_complete_or_client_stopped();
+    /// @}
+
+    /// \brief Cancel the current or next reconnect delay for the server
+    /// associated with this session.
+    ///
+    /// When the network connection is severed, or an attempt to establish
+    /// connection fails, a certain delay will take effect before the client
+    /// will attempt to reestablish the connection. This delay will generally
+    /// grow with the number of unsuccessful reconnect attempts, and can grow to
+    /// over a minute. In some cases however, the application will know when it
+    /// is a good time to stop waiting and retry immediately. One example is
+    /// when a device has been offline for a while, and the operating system
+    /// then tells the application that network connectivity has been restored.
+    ///
+    /// Clearly, this function should not be called too often and over extended
+    /// periods of time, as that would effectively disable the built-in "server
+    /// hammering" protection.
+    ///
+    /// It is an error to call this function before bind() has been called, and
+    /// has returned.
+    ///
+    /// This function is fully thread-safe. That is, it may be called by any
+    /// thread, and by multiple threads concurrently.
+    void cancel_reconnect_delay();
+
+    /// \brief Change address of server for this session.
+    void override_server(std::string address, port_type);
+
+private:
+    class Impl;
+    Impl* m_impl = nullptr;
+
+    void abandon() noexcept;
+    void async_wait_for(bool upload_completion, bool download_completion,
+                        WaitOperCompletionHandler);
+};
+
+
+/// \brief Protocol errors discovered by the client.
+///
+/// These errors will terminate the network connection (disconnect all sessions
+/// associated with the affected connection), and the error will be reported to
+/// the application via the connection state change listeners of the affected
+/// sessions.
+enum class Client::Error {
+    connection_closed           = 100, ///< Connection closed (no error)
+    unknown_message             = 101, ///< Unknown type of input message
+    bad_syntax                  = 102, ///< Bad syntax in input message head
+    limits_exceeded             = 103, ///< Limits exceeded in input message
+    bad_session_ident           = 104, ///< Bad session identifier in input message
+    bad_message_order           = 105, ///< Bad input message order
+    bad_file_ident_pair         = 106, ///< Bad file identifier pair (ALLOC)
+    bad_progress                = 107, ///< Bad progress information (DOWNLOAD)
+    bad_changeset_header_syntax = 108, ///< Bad syntax in changeset header (DOWNLOAD)
+    bad_changeset_size          = 109, ///< Bad changeset size in changeset header (DOWNLOAD)
+    bad_origin_file_ident       = 110, ///< Bad origin file identifier in changeset header (DOWNLOAD)
+    bad_server_version          = 111, ///< Bad server version in changeset header (DOWNLOAD)
+    bad_changeset               = 112, ///< Bad changeset (DOWNLOAD)
+    bad_request_ident           = 113, ///< Bad request identifier (MARK)
+    bad_error_code              = 114, ///< Bad error code (ERROR),
+    bad_compression             = 115, ///< Bad compression (DOWNLOAD)
+    bad_client_version          = 116, ///< Bad last integrated client version in changeset header (DOWNLOAD)
+    ssl_server_cert_rejected    = 117, ///< SSL server certificate rejected
+    pong_timeout                = 118, ///< Timeout on reception of PONG respone message
+};
+
+const std::error_category& client_error_category() noexcept;
+
+std::error_code make_error_code(Client::Error) noexcept;
+
+} // namespace sync
+} // namespace realm
+
+namespace std {
+
+template<> struct is_error_code_enum<realm::sync::Client::Error> {
+    static const bool value = true;
+};
+
+} // namespace std
+
+namespace realm {
+namespace sync {
+
+
+
+// Implementation
+
+class BadServerUrl: public std::exception {
+public:
+    const char* what() const noexcept override
+    {
+        return "Bad server URL";
+    }
+};
+
+inline Session::Session(Session&& sess) noexcept:
+    m_impl{sess.m_impl}
+{
+    sess.m_impl = nullptr;
+}
+
+inline Session::Session() noexcept
+{
+}
+
+inline Session::~Session() noexcept
+{
+    if (m_impl)
+        abandon();
+}
+
+inline Session& Session::operator=(Session&& sess) noexcept
+{
+    if (m_impl)
+        abandon();
+    m_impl = sess.m_impl;
+    sess.m_impl = nullptr;
+    return *this;
+}
+
+inline void Session::detach() noexcept
+{
+    if (m_impl)
+        abandon();
+    m_impl = nullptr;
+}
+
+inline void Session::set_error_handler(std::function<ErrorHandler> handler)
+{
+    auto handler_2 = [handler=std::move(handler)](ConnectionState state,
+                                                  const ErrorInfo* error_info) {
+        if (state != ConnectionState::disconnected)
+            return;
+        REALM_ASSERT(error_info);
+        std::error_code ec = error_info->error_code;
+        bool is_fatal = error_info->is_fatal;
+        const std::string& detailed_message = error_info->detailed_message;
+        handler(ec, is_fatal, detailed_message); // Throws
+    };
+    set_connection_state_change_listener(std::move(handler_2)); // Throws
+}
+
+inline void Session::async_wait_for_sync_completion(WaitOperCompletionHandler handler)
+{
+    bool upload_completion = true, download_completion = true;
+    async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
+}
+
+inline void Session::async_wait_for_upload_completion(WaitOperCompletionHandler handler)
+{
+    bool upload_completion = true, download_completion = false;
+    async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
+}
+
+inline void Session::async_wait_for_download_completion(WaitOperCompletionHandler handler)
+{
+    bool upload_completion = false, download_completion = true;
+    async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
+}
+
+} // namespace sync
+} // namespace realm
+
+#endif // REALM_SYNC_CLIENT_HPP