123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- #ifndef REALM_SYNC_PROTOCOL_HPP
- #define REALM_SYNC_PROTOCOL_HPP
- #include <cstdint>
- #include <system_error>
- #include <realm/replication.hpp>
- // NOTE: The protocol specification is in `/doc/protocol.md`
- namespace realm {
- namespace sync {
- // Protocol versions:
- //
- // 1 Initial version, matching io.realm.sync-30, but not including query-based
- // sync, serialized transactions, and state realms (async open).
- //
- // 2 Restored erase-always-wins OT behavior.
- //
- // 3 Support for Mixed, TypeLinks, Set, and Dictionary columns.
- //
- // XX Changes:
- // - TBD
- //
- constexpr int get_current_protocol_version() noexcept
- {
- return 3;
- }
- constexpr const char* get_websocket_protocol_prefix() noexcept
- {
- return "com.mongodb.realm-sync/";
- }
- /// Supported protocol envelopes:
- ///
- /// Alternative (*)
- /// Name Envelope URL scheme Default port default port
- /// ------------------------------------------------------------------------
- /// realm WebSocket realm: 7800 80
- /// realms WebSocket + SSL realms: 7801 443
- /// ws WebSocket ws: 80
- /// wss WebSocket + SSL wss: 443
- ///
- /// *) When Client::Config::enable_default_port_hack is true
- ///
- enum class ProtocolEnvelope { realm, realms, ws, wss };
- inline bool is_ssl(ProtocolEnvelope protocol) noexcept
- {
- switch (protocol) {
- case ProtocolEnvelope::realm:
- case ProtocolEnvelope::ws:
- break;
- case ProtocolEnvelope::realms:
- case ProtocolEnvelope::wss:
- return true;
- }
- return false;
- }
- // These integer types are selected so that they accomodate the requirements of
- // the protocol specification (`/doc/protocol.md`).
- //
- // clang-format off
- using file_ident_type = std::uint_fast64_t;
- using version_type = Replication::version_type;
- using salt_type = std::int_fast64_t;
- using timestamp_type = std::uint_fast64_t;
- using session_ident_type = std::uint_fast64_t;
- using request_ident_type = std::uint_fast64_t;
- using milliseconds_type = std::int_fast64_t;
- // clang-format on
- constexpr file_ident_type get_max_file_ident()
- {
- return 0x0'7FFF'FFFF'FFFF'FFFF;
- }
- struct SaltedFileIdent {
- file_ident_type ident;
- /// History divergence and identity spoofing protection.
- salt_type salt;
- };
- struct SaltedVersion {
- version_type version;
- /// History divergence protection.
- salt_type salt;
- };
- /// \brief A client's reference to a position in the server-side history.
- ///
- /// A download cursor refers to a position in the server-side history. If
- /// `server_version` is zero, the position is at the beginning of the history,
- /// otherwise the position is after the entry whose changeset produced that
- /// version. In general, positions are to be understood as places between two
- /// adjacent history entries.
- ///
- /// `last_integrated_client_version` is the version produced on the client by
- /// the last changeset that was sent to the server and integrated into the
- /// server-side Realm state at the time indicated by the history position
- /// specified by `server_version`, or zero if no changesets from the client were
- /// integrated by the server at that point in time.
- struct DownloadCursor {
- version_type server_version;
- version_type last_integrated_client_version;
- };
- /// Checks that `dc.last_integrated_client_version` is zero if
- /// `dc.server_version` is zero.
- bool is_consistent(DownloadCursor dc) noexcept;
- /// Checks that `a.last_integrated_client_version` and
- /// `b.last_integrated_client_version` are equal, if `a.server_version` and
- /// `b.server_version` are equal. Otherwise checks that
- /// `a.last_integrated_client_version` is less than, or equal to
- /// `b.last_integrated_client_version`, if `a.server_version` is less than
- /// `b.server_version`. Otherwise checks that `a.last_integrated_client_version`
- /// is greater than, or equal to `b.last_integrated_client_version`.
- bool are_mutually_consistent(DownloadCursor a, DownloadCursor b) noexcept;
- /// \brief The server's reference to a position in the client-side history.
- ///
- /// An upload cursor refers to a position in the client-side history. If
- /// `client_version` is zero, the position is at the beginning of the history,
- /// otherwise the position is after the entry whose changeset produced that
- /// version. In general, positions are to be understood as places between two
- /// adjacent history entries.
- ///
- /// `last_integrated_server_version` is the version produced on the server by
- /// the last changeset that was sent to the client and integrated into the
- /// client-side Realm state at the time indicated by the history position
- /// specified by `client_version`, or zero if no changesets from the server were
- /// integrated by the client at that point in time.
- struct UploadCursor {
- version_type client_version;
- version_type last_integrated_server_version;
- };
- /// Checks that `uc.last_integrated_server_version` is zero if
- /// `uc.client_version` is zero.
- bool is_consistent(UploadCursor uc) noexcept;
- /// Checks that `a.last_integrated_server_version` and
- /// `b.last_integrated_server_version` are equal, if `a.client_version` and
- /// `b.client_version` are equal. Otherwise checks that
- /// `a.last_integrated_server_version` is less than, or equal to
- /// `b.last_integrated_server_version`, if `a.client_version` is less than
- /// `b.client_version`. Otherwise checks that `a.last_integrated_server_version`
- /// is greater than, or equal to `b.last_integrated_server_version`.
- bool are_mutually_consistent(UploadCursor a, UploadCursor b) noexcept;
- /// A client's record of the current point of progress of the synchronization
- /// process. The client must store this persistently in the local Realm file.
- struct SyncProgress {
- /// The last server version that the client has heard about.
- SaltedVersion latest_server_version = {0, 0};
- /// The last server version integrated, or about to be integrated by the
- /// client.
- DownloadCursor download = {0, 0};
- /// The last client version integrated by the server.
- UploadCursor upload = {0, 0};
- };
- /// \brief Protocol errors discovered by the server, and reported to the client
- /// by way of ERROR messages.
- ///
- /// These errors will be reported to the client-side application via the error
- /// handlers of the affected sessions.
- ///
- /// ATTENTION: Please remember to update is_session_level_error() when
- /// adding/removing error codes.
- enum class ProtocolError {
- // clang-format off
- // Connection level and protocol errors
- connection_closed = 100, // Connection closed (no error)
- other_error = 101, // Other connection level error
- unknown_message = 102, // Unknown type of input message
- bad_syntax = 103, // Bad syntax in input message head
- limits_exceeded = 104, // Limits exceeded in input message
- wrong_protocol_version = 105, // Wrong protocol version (CLIENT) (obsolete)
- bad_session_ident = 106, // Bad session identifier in input message
- reuse_of_session_ident = 107, // Overlapping reuse of session identifier (BIND)
- bound_in_other_session = 108, // Client file bound in other session (IDENT)
- bad_message_order = 109, // Bad input message order
- bad_decompression = 110, // Error in decompression (UPLOAD)
- bad_changeset_header_syntax = 111, // Bad syntax in a changeset header (UPLOAD)
- bad_changeset_size = 112, // Bad size specified in changeset header (UPLOAD)
- bad_changesets = 113, // Bad changesets (UPLOAD)
- // Session level errors
- session_closed = 200, // Session closed (no error)
- other_session_error = 201, // Other session level error
- token_expired = 202, // Access token expired
- bad_authentication = 203, // Bad user authentication (BIND, REFRESH)
- illegal_realm_path = 204, // Illegal Realm path (BIND)
- no_such_realm = 205, // No such Realm (BIND)
- permission_denied = 206, // Permission denied (BIND, REFRESH)
- bad_server_file_ident = 207, // Bad server file identifier (IDENT) (obsolete!)
- bad_client_file_ident = 208, // Bad client file identifier (IDENT)
- bad_server_version = 209, // Bad server version (IDENT, UPLOAD, TRANSACT)
- bad_client_version = 210, // Bad client version (IDENT, UPLOAD)
- diverging_histories = 211, // Diverging histories (IDENT)
- bad_changeset = 212, // Bad changeset (UPLOAD)
- superseded = 213, // Superseded by new session for same client-side file (deprecated)
- disabled_session = 213, // Alias for `superseded` (deprecated)
- partial_sync_disabled = 214, // Partial sync disabled (BIND)
- unsupported_session_feature = 215, // Unsupported session-level feature
- bad_origin_file_ident = 216, // Bad origin file identifier (UPLOAD)
- bad_client_file = 217, // Synchronization no longer possible for client-side file
- server_file_deleted = 218, // Server file was deleted while session was bound to it
- client_file_blacklisted = 219, // Client file has been blacklisted (IDENT)
- user_blacklisted = 220, // User has been blacklisted (BIND)
- transact_before_upload = 221, // Serialized transaction before upload completion
- client_file_expired = 222, // Client file has expired
- user_mismatch = 223, // User mismatch for client file identifier (IDENT)
- too_many_sessions = 224, // Too many sessions in connection (BIND)
- invalid_schema_change = 225, // Invalid schema change (UPLOAD)
- // clang-format on
- };
- constexpr bool is_session_level_error(ProtocolError);
- /// Returns null if the specified protocol error code is not defined by
- /// ProtocolError.
- const char* get_protocol_error_message(int error_code) noexcept;
- const std::error_category& protocol_error_category() noexcept;
- std::error_code make_error_code(ProtocolError) noexcept;
- } // namespace sync
- } // namespace realm
- namespace std {
- template <>
- struct is_error_code_enum<realm::sync::ProtocolError> {
- static const bool value = true;
- };
- } // namespace std
- namespace realm {
- namespace sync {
- // Implementation
- inline bool is_consistent(DownloadCursor dc) noexcept
- {
- return (dc.server_version != 0 || dc.last_integrated_client_version == 0);
- }
- inline bool are_mutually_consistent(DownloadCursor a, DownloadCursor b) noexcept
- {
- if (a.server_version < b.server_version)
- return (a.last_integrated_client_version <= b.last_integrated_client_version);
- if (a.server_version > b.server_version)
- return (a.last_integrated_client_version >= b.last_integrated_client_version);
- return (a.last_integrated_client_version == b.last_integrated_client_version);
- }
- inline bool is_consistent(UploadCursor uc) noexcept
- {
- return (uc.client_version != 0 || uc.last_integrated_server_version == 0);
- }
- inline bool are_mutually_consistent(UploadCursor a, UploadCursor b) noexcept
- {
- if (a.client_version < b.client_version)
- return (a.last_integrated_server_version <= b.last_integrated_server_version);
- if (a.client_version > b.client_version)
- return (a.last_integrated_server_version >= b.last_integrated_server_version);
- return (a.last_integrated_server_version == b.last_integrated_server_version);
- }
- constexpr bool is_session_level_error(ProtocolError error)
- {
- return int(error) >= 200 && int(error) <= 299;
- }
- } // namespace sync
- } // namespace realm
- #endif // REALM_SYNC_PROTOCOL_HPP
|