added iOS source code
[wl-app.git] / iOS / Pods / Realm / include / core / realm / sync / client.hpp
1 /*************************************************************************
2  *
3  * REALM CONFIDENTIAL
4  * __________________
5  *
6  *  [2011] - [2012] Realm Inc
7  *  All Rights Reserved.
8  *
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.
18  *
19  **************************************************************************/
20 #ifndef REALM_SYNC_CLIENT_HPP
21 #define REALM_SYNC_CLIENT_HPP
22
23 #include <stdint.h>
24 #include <memory>
25 #include <utility>
26 #include <functional>
27 #include <exception>
28 #include <string>
29
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>
34
35 namespace realm {
36 namespace sync {
37
38 class Client {
39 public:
40     enum class Error;
41
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).
47         normal,
48
49         /// Delay reconnect attempts indefinitely. For testing purposes only.
50         ///
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.
57         never,
58
59         /// Never delay reconnect attempts. Perform them immediately. For
60         /// testing purposes only.
61         immediate
62     };
63
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
67
68     struct Config {
69         Config() {}
70
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;
75
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;
83
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;
88
89         /// For testing purposes only.
90         ReconnectMode reconnect_mode = ReconnectMode::normal;
91
92         /// Create a separate connection for each session. For testing purposes
93         /// only.
94         ///
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;
98
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
103         /// only.
104         bool dry_run = false;
105
106         /// The default changeset cooker to be used by new sessions. Can be
107         /// overridden by Session::Config::changeset_cooker.
108         ///
109         /// \sa make_client_history(), TrivialChangesetCooker.
110         std::shared_ptr<ClientHistory::ChangesetCooker> changeset_cooker;
111
112         /// The number of ms between periodic keep-alive pings.
113         std::uint_fast64_t ping_keepalive_period_ms = default_ping_keepalive_period_ms;
114
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;
117
118         /// The number of ms to wait for urgent pongs.
119         std::uint_fast64_t pong_urgent_timeout_ms = default_pong_urgent_timeout_ms;
120
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
126         /// consumption.
127         bool enable_upload_log_compaction = true;
128
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;
134     };
135
136     /// \throw util::EventLoop::Implementation::NotAvailable if no event loop
137     /// implementation was specified, and
138     /// util::EventLoop::Implementation::get_default() throws it.
139     Client(Config = {});
140     Client(Client&&) noexcept;
141     ~Client() noexcept;
142
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
145     /// calls stop().
146     void run();
147
148     /// See run().
149     ///
150     /// Thread-safe.
151     void stop() noexcept;
152
153     /// \brief Cancel current or next reconnect delay for all servers.
154     ///
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.
158     ///
159     /// Thread-safe.
160     void cancel_reconnect_delay();
161
162     /// \brief Wait for session termination to complete.
163     ///
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().
169     ///
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.
174     ///
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.
181     ///
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.
185     ///
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.
189     ///
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.
193     ///
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();
197
198 private:
199     class Impl;
200     std::unique_ptr<Impl> m_impl;
201     friend class Session;
202 };
203
204
205 /// Supported protocols:
206 ///
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)
211 ///
212 enum class Protocol {
213     realm,
214     realm_ssl
215 };
216
217
218 class BadServerUrl; // Exception
219
220
221 /// \brief Client-side representation of a Realm file synchronization session.
222 ///
223 /// A synchronization session deals with precisely one local Realm file. To
224 /// synchronize multiple local Realm files, you need multiple sessions.
225 ///
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.
230 ///
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.
236 ///
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.
250 ///
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.
256 ///
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.
275 class Session {
276 public:
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,
290                                    size_t pem_size,
291                                    int preverify_ok,
292                                    int depth);
293
294     class Config {
295     public:
296         Config() {}
297
298         /// server_address is the fully qualified host name, or IP address of
299         /// the server.
300         std::string server_address = "localhost";
301
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;
306
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 = "/";
319
320         /// The protocol used for communicating with the server. See \ref Protocol.
321         Protocol protocol = Protocol::realm;
322
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";
329
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;
340
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;
345
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.
350         ///
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).
357         ///
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;
362
363         /// If Client::Config::ssl_verify_callback is set, that function is called
364         /// to verify the certificate, unless verify_servers_ssl_certificate is
365         /// false.
366
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
370         /// is None.
371         ///
372         /// The signature of ssl_verify_callback is
373         ///
374         /// bool(const std::string& server_address,
375         ///      port_type server_port,
376         ///      const char* pem_data,
377         ///      size_t pem_size,
378         ///      int preverify_ok,
379         ///      int depth);
380         ///
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
398         /// length - 1.
399         ///
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.
407         ///
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.
412         ///
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;
417
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;
421
422         /// If not null, overrides whatever is specified by
423         /// Client::Config::changeset_cooker.
424         ///
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()).
428         ///
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
433         /// more on this.
434         ///
435         /// \sa make_client_history(), TrivialChangesetCooker.
436         std::shared_ptr<ClientHistory::ChangesetCooker> changeset_cooker;
437
438         /// The encryption key the SharedGroup will be opened with.
439         Optional<std::array<char, 64>> encryption_key;
440
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;
444     };
445
446     /// \brief Start a new session for the specified client-side Realm.
447     ///
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().
451     ///
452     /// \param realm_path The file-system path of a local client-side Realm
453     /// file.
454     Session(Client&, std::string realm_path, Config = {});
455
456     /// This leaves the right-hand side session object detached. See "Thread
457     /// safety" section under detach().
458     Session(Session&&) noexcept;
459
460     /// Create a detached session object (see detach()).
461     Session() noexcept;
462
463     /// Implies detachment. See "Thread safety" section under detach().
464     ~Session() noexcept;
465
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
469     /// under detach().
470     Session& operator=(Session&&) noexcept;
471
472     /// Detach this sesion object from the client object (Client). If the
473     /// session object is already detached, this function has no effect
474     /// (idempotency).
475     ///
476     /// Detachment initiates session termination, which is an event that takes
477     /// place shortly therafter in the context of the client's event loop
478     /// thread.
479     ///
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.
483     ///
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;
491
492     /// \brief Set a function to be called when the local Realm has changed due
493     /// to integration of a downloaded changeset.
494     ///
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.
498     ///
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().
503     ///
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.
507     ///
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.
511     ///
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>);
517
518     /// \brief Set a handler to monitor the state of download and upload
519     /// progress.
520     ///
521     /// The handler must have signature
522     ///
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);
526     ///
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.
530     ///
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.
534     ///
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.
538     ///
539     /// Typical uses of this function:
540     ///
541     /// Upload completion can be checked by
542     ///
543     ///    bool upload_complete = (uploaded_bytes == uploadable_bytes);
544     ///
545     /// Download completion could be checked by
546     ///
547     ///     bool download_complete = (downloaded_bytes == downloadable_bytes);
548     ///
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.
553     ///
554     ///     bool download_complete = (downloaded_bytes == cached_downloadable_bytes);
555     ///
556     /// Upload progress can be calculated by caching an initial value of
557     /// uploaded_bytes from the last, or next, callback. Then
558     ///
559     ///     double upload_progress =
560     ///        (uploaded_bytes - initial_uploaded_bytes)
561     ///       -------------------------------------------
562     ///       (uploadable_bytes - initial_uploaded_bytes)
563     ///
564     /// Download progress can be calculates similarly:
565     ///
566     ///     double download_progress =
567     ///        (downloaded_bytes - initial_downloaded_bytes)
568     ///       -----------------------------------------------
569     ///       (downloadable_bytes - initial_downloaded_bytes)
570     ///
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
578     /// been received.
579     ///
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).
583     ///
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.
587     ///
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>);
593
594     enum class ConnectionState { disconnected, connecting, connected };
595
596     /// \brief Information about an error causing a session to be temporarily
597     /// disconnected from the server.
598     ///
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).
605     ///
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
612     /// WebSocket).
613     ///
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
619     /// sufficient.
620     ///
621     /// \sa set_connection_state_change_listener().
622     struct ErrorInfo {
623         std::error_code error_code;
624         bool is_fatal;
625         const std::string& detailed_message;
626     };
627
628     using ConnectionStateChangeListener = void(ConnectionState, const ErrorInfo*);
629
630     /// \brief Install a connection state change listener.
631     ///
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.
639     ///
640     /// Whenever the installed function is called, an ErrorInfo object is passed
641     /// when, and only when the passed state is ConnectionState::disconnected.
642     ///
643     /// When multiple sessions share a single connection, the state changes will
644     /// be reported for each session in turn.
645     ///
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().
650     ///
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.
654     ///
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.
658     ///
659     /// CAUTION: The specified callback function may get called before the call
660     /// to bind() returns, and it may get called (or continue to execute) after
661     /// the session object is destroyed. Please see "Callback semantics" section
662     /// under Session for more on this.
663     void set_connection_state_change_listener(std::function<ConnectionStateChangeListener>);
664
665     //@{
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>);
669     //@}
670
671     /// @{ \brief Bind this session to the specified server side Realm.
672     ///
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.
676     ///
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.
682     ///
683     /// Note: It is an error if this function is called more than once per
684     /// Session object.
685     ///
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.
689     ///
690     /// bind() binds this session to the specified server side Realm using the
691     /// parameters specified in the Session::Config object.
692     ///
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.
702     ///
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.
707     ///
708     /// \throw BadServerUrl if the specified server URL is malformed.
709     void bind();
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);
714     /// @}
715
716     /// \brief Refresh the user token associated with this session.
717     ///
718     /// This causes the REFRESH protocol message to be sent to the server. See
719     /// \ref Protocol.
720     ///
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.
727     ///
728     /// It is an error to call this function before calling `Client::bind()`.
729     ///
730     /// Note: This function is thread-safe.
731     ///
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);
735
736     /// \brief Inform the synchronization agent about changes of local origin.
737     ///
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.
741     ///
742     /// It is an error to call this function before bind() has been called, and
743     /// has returned.
744     ///
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);
748
749     /// @{ \brief Wait for upload, download, or upload+download completion.
750     ///
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.
756     ///
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.
769     ///
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.
779     ///
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.
786     ///
787     /// It is an error to call these functions before bind() has been called,
788     /// and has returned.
789     ///
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().
794     ///
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.
804     ///
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.
809     ///
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);
815     /// @}
816
817     /// @{ \brief Synchronous wait for upload or download completion.
818     ///
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
824     /// first.
825     ///
826     /// It is an error to call these functions before bind() has been called,
827     /// and has returned.
828     ///
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.
833     ///
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.
837     ///
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();
842     /// @}
843
844     /// \brief Cancel the current or next reconnect delay for the server
845     /// associated with this session.
846     ///
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.
855     ///
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.
859     ///
860     /// It is an error to call this function before bind() has been called, and
861     /// has returned.
862     ///
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();
866
867     /// \brief Change address of server for this session.
868     void override_server(std::string address, port_type);
869
870 private:
871     class Impl;
872     Impl* m_impl = nullptr;
873
874     void abandon() noexcept;
875     void async_wait_for(bool upload_completion, bool download_completion,
876                         WaitOperCompletionHandler);
877 };
878
879
880 /// \brief Protocol errors discovered by the client.
881 ///
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
885 /// sessions.
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
906 };
907
908 const std::error_category& client_error_category() noexcept;
909
910 std::error_code make_error_code(Client::Error) noexcept;
911
912 } // namespace sync
913 } // namespace realm
914
915 namespace std {
916
917 template<> struct is_error_code_enum<realm::sync::Client::Error> {
918     static const bool value = true;
919 };
920
921 } // namespace std
922
923 namespace realm {
924 namespace sync {
925
926
927
928 // Implementation
929
930 class BadServerUrl: public std::exception {
931 public:
932     const char* what() const noexcept override
933     {
934         return "Bad server URL";
935     }
936 };
937
938 inline Session::Session(Session&& sess) noexcept:
939     m_impl{sess.m_impl}
940 {
941     sess.m_impl = nullptr;
942 }
943
944 inline Session::Session() noexcept
945 {
946 }
947
948 inline Session::~Session() noexcept
949 {
950     if (m_impl)
951         abandon();
952 }
953
954 inline Session& Session::operator=(Session&& sess) noexcept
955 {
956     if (m_impl)
957         abandon();
958     m_impl = sess.m_impl;
959     sess.m_impl = nullptr;
960     return *this;
961 }
962
963 inline void Session::detach() noexcept
964 {
965     if (m_impl)
966         abandon();
967     m_impl = nullptr;
968 }
969
970 inline void Session::set_error_handler(std::function<ErrorHandler> handler)
971 {
972     auto handler_2 = [handler=std::move(handler)](ConnectionState state,
973                                                   const ErrorInfo* error_info) {
974         if (state != ConnectionState::disconnected)
975             return;
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
981     };
982     set_connection_state_change_listener(std::move(handler_2)); // Throws
983 }
984
985 inline void Session::async_wait_for_sync_completion(WaitOperCompletionHandler handler)
986 {
987     bool upload_completion = true, download_completion = true;
988     async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
989 }
990
991 inline void Session::async_wait_for_upload_completion(WaitOperCompletionHandler handler)
992 {
993     bool upload_completion = true, download_completion = false;
994     async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
995 }
996
997 inline void Session::async_wait_for_download_completion(WaitOperCompletionHandler handler)
998 {
999     bool upload_completion = false, download_completion = true;
1000     async_wait_for(upload_completion, download_completion, std::move(handler)); // Throws
1001 }
1002
1003 } // namespace sync
1004 } // namespace realm
1005
1006 #endif // REALM_SYNC_CLIENT_HPP