Blame view

Pods/Realm/include/core/realm/sync/protocol.hpp 11.6 KB
75d24c15   yangbin   123
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
  #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