1 /*************************************************************************
6 * [2011] - [2016] Realm Inc
9 * NOTICE: All information contained herein is, and remains
10 * the property of Realm Incorporated and its suppliers,
11 * if any. The intellectual and technical concepts contained
12 * herein are proprietary to Realm Incorporated
13 * and its suppliers and may be covered by U.S. and Foreign Patents,
14 * patents in process, and are protected by trade secret or copyright law.
15 * Dissemination of this information or reproduction of this material
16 * is strictly forbidden unless prior written permission is obtained
17 * from Realm Incorporated.
19 **************************************************************************/
20 #ifndef REALM_SYNC_PROTOCOL_HPP
21 #define REALM_SYNC_PROTOCOL_HPP
23 #include <system_error>
25 #include <realm/util/buffer_stream.hpp>
26 #include <realm/util/compression.hpp>
27 #include <realm/util/logger.hpp>
28 #include <realm/util/memory_stream.hpp>
29 #include <realm/util/optional.hpp>
30 #include <realm/impl/clamped_hex_dump.hpp>
31 #include <realm/sync/transform.hpp>
32 #include <realm/sync/history.hpp>
34 #include <realm/sync/client.hpp> // Get rid of this?
37 // NOTE: The protocol specification is in `/doc/protocol.md`
47 // 2 Introduces the UNBOUND message (sent from server to client in
48 // response to a BIND message).
50 // 3 Introduces the ERROR message (sent from server to client before the
51 // server closes a connection). Introduces MARK message from client to
52 // server, and MARK response message from server to client as a way for the
53 // client to wait for download to complete.
55 // 4 User token and signature are now passed as a single string (see
56 // /doc/protocol.md for details). Also, `application_ident` parameter
57 // removed from IDENT message.
59 // 5 IDENT message renamed to CLIENT, and ALLOC message (client->server)
60 // renamed to IDENT. Also, <client info> parameter added to CLIENT
61 // message. Also, the protocol has been changed to make the clients
62 // acquisition of a server allocated file identifier pair be part of a
63 // session from the servers point of view. File identifier and version
64 // parameters moved from the BIND message to a new IDENT message sent by
65 // client when it has obtained the file identifier pair. Both the new IDENT
66 // message and the ALLOC message sent by the server are now properly
67 // associated with a session.
69 // 6 Server session IDs have been added to the IDENT, DOWNLOAD, and PROGRESS
70 // messages, and the "Divergent history" error code was added as an
71 // indication that a server version / session ID pair does not match the
74 // 7 FIXME: Who introduced version 7? Please describe what changed.
76 // 8 Error code (`bad_authentication`) moved from 200-range to 300-range
77 // because it is now session specific. Other error codes were renumbered.
79 // 9 New format of the DOWNLOAD message to support progress reporting on the
82 // 10 Error codes reordered (now categorized as either connection or session
85 // 11 Bugfixes in Link List and ChangeLinkTargets merge rules, that
86 // make previous versions incompatible.
88 // 12 FIXME What was 12?
90 // 13 Bugfixes in Link List and ChangeLinkTargets merge rules, that
91 // make previous versions incompatible.
93 // 14 Further bugfixes related to primary keys and link lists. Add support for
96 // 15 Deleting an object with a primary key deletes all objects on other
97 // with the same primary key.
99 // 16 Downloadable bytes added to DOWNLOAD message. It is used for download progress
102 // 17 Added PING and PONG messages. It is used for rtt monitoring and dead
103 // connection detection by both the client and the server.
105 // 18 Enhanced the session_ident to accept values of size up to at least 63 bits.
107 // 19 New instruction log format with stable object IDs and arrays of
108 // primitives (Generalized LinkList* commands to Container* commands)
109 // Message format is identical to version 18.
111 // 20 Added support for log compaction in DOWNLOAD message.
113 // 21 Removed "class_" prefix in instructions referencing tables.
115 // 22 Fixed a bug in the merge rule of MOVE vs SWAP.
117 constexpr int get_current_protocol_version() noexcept
122 /// \brief Protocol errors discovered by the server, and reported to the client
123 /// by way of ERROR messages.
125 /// These errors will be reported to the client-side application via the error
126 /// handlers of the affected sessions.
128 /// ATTENTION: Please remember to update is_session_level_error() when
129 /// adding/removing error codes.
130 enum class ProtocolError {
131 // Connection level and protocol errors
132 connection_closed = 100, // Connection closed (no error)
133 other_error = 101, // Other connection level error
134 unknown_message = 102, // Unknown type of input message
135 bad_syntax = 103, // Bad syntax in input message head
136 limits_exceeded = 104, // Limits exceeded in input message
137 wrong_protocol_version = 105, // Wrong protocol version (CLIENT)
138 bad_session_ident = 106, // Bad session identifier in input message
139 reuse_of_session_ident = 107, // Overlapping reuse of session identifier (BIND)
140 bound_in_other_session = 108, // Client file bound in other session (IDENT)
141 bad_message_order = 109, // Bad input message order
142 bad_decompression = 110, // Error in decompression (UPLOAD)
143 bad_changeset_header_syntax = 111, // Bad syntax in a changeset header (UPLOAD)
144 bad_changeset_size = 112, // Bad size specified in changeset header (UPLOAD)
145 bad_changesets = 113, // Bad changesets (UPLOAD)
147 // Session level errors
148 session_closed = 200, // Session closed (no error)
149 other_session_error = 201, // Other session level error
150 token_expired = 202, // Access token expired
151 bad_authentication = 203, // Bad user authentication (BIND, REFRESH)
152 illegal_realm_path = 204, // Illegal Realm path (BIND)
153 no_such_realm = 205, // No such Realm (BIND)
154 permission_denied = 206, // Permission denied (BIND, REFRESH)
155 bad_server_file_ident = 207, // Bad server file identifier (IDENT) (obsolete!)
156 bad_client_file_ident = 208, // Bad client file identifier (IDENT)
157 bad_server_version = 209, // Bad server version (IDENT, UPLOAD)
158 bad_client_version = 210, // Bad client version (IDENT, UPLOAD)
159 diverging_histories = 211, // Diverging histories (IDENT)
160 bad_changeset = 212, // Bad changeset (UPLOAD)
161 disabled_session = 213, // Disabled session
162 partial_sync_disabled = 214, // Partial sync disabled (BIND)
165 inline constexpr bool is_session_level_error(ProtocolError error)
167 return int(error) >= 200 && int(error) <= 299;
170 /// Returns null if the specified protocol error code is not defined by
172 const char* get_protocol_error_message(int error_code) noexcept;
174 const std::error_category& protocol_error_category() noexcept;
176 std::error_code make_error_code(ProtocolError) noexcept;
183 template<> struct is_error_code_enum<realm::sync::ProtocolError> {
184 static const bool value = true;
193 using OutputBuffer = util::ResettableExpandableBufferOutputStream;
194 using session_ident_type = uint_fast64_t;
195 using file_ident_type = uint_fast64_t;
196 using version_type = uint_fast64_t;
197 using timestamp_type = uint_fast64_t;
198 using request_ident_type = uint_fast64_t;
199 using ReceivedChangesets = std::vector<Transformer::RemoteChangeset>;
202 class ClientProtocol {
204 util::Logger& logger;
207 unknown_message = 101, // Unknown type of input message
208 bad_syntax = 102, // Bad syntax in input message head
209 limits_exceeded = 103, // Limits exceeded in input message
210 bad_changeset_header_syntax = 108, // Bad syntax in changeset header (DOWNLOAD)
211 bad_changeset_size = 109, // Bad changeset size in changeset header (DOWNLOAD)
212 bad_server_version = 111, // Bad server version in changeset header (DOWNLOAD)
213 bad_error_code = 114, ///< Bad error code (ERROR)
214 bad_decompression = 115, // Error in decompression (DOWNLOAD)
217 ClientProtocol(util::Logger& logger);
220 /// Messages sent by the client.
222 void make_client_message(OutputBuffer& out, const std::string& client_info);
224 void make_bind_message(OutputBuffer& out, session_ident_type session_ident,
225 const std::string& server_path,
226 const std::string& signed_user_token,
227 bool need_file_ident_pair);
229 void make_refresh_message(OutputBuffer& out, session_ident_type session_ident,
230 const std::string& signed_user_token);
232 void make_ident_message(OutputBuffer& out, session_ident_type session_ident,
233 file_ident_type server_file_ident,
234 file_ident_type client_file_ident,
235 int_fast64_t client_file_ident_secret,
236 SyncProgress progress);
238 class UploadMessageBuilder {
240 util::Logger& logger;
242 UploadMessageBuilder(util::Logger& logger,
243 OutputBuffer& body_buffer,
244 std::vector<char>& compression_buffer,
245 util::compression::CompressMemoryArena& compress_memory_arena);
247 void add_changeset(version_type client_version, version_type server_version,
248 timestamp_type timestamp, ChunkedBinaryData changeset);
250 void make_upload_message(OutputBuffer& out, session_ident_type session_ident);
253 size_t m_num_changesets = 0;
254 OutputBuffer& m_body_buffer;
255 std::vector<char>& m_compression_buffer;
256 util::compression::CompressMemoryArena& m_compress_memory_arena;
259 UploadMessageBuilder make_upload_message_builder();
261 void make_upload_message(OutputBuffer& out, session_ident_type session_ident,
262 version_type client_version, version_type server_version,
263 size_t changeset_size, timestamp_type timestamp,
264 const std::unique_ptr<char[]>& body_buffer);
266 void make_unbind_message(OutputBuffer& out, session_ident_type session_ident);
268 void make_mark_message(OutputBuffer& out, session_ident_type session_ident,
269 request_ident_type request_ident);
271 void make_ping(OutputBuffer& out, uint_fast64_t timestamp, uint_fast64_t rtt);
274 // Messages received by the client.
276 // parse_pong_received takes a (WebSocket) pong and parses it.
277 // The result of the parsing is handled by an object of type Connection.
278 // Typically, Connection would be the Connection class from client.cpp
279 template <typename Connection>
280 void parse_pong_received(Connection& connection, const char* data, size_t size)
282 util::MemoryInputStream in;
283 in.set_buffer(data, data + size);
284 in.unsetf(std::ios_base::skipws);
286 uint_fast64_t timestamp;
289 in >> timestamp >> newline;
290 bool good_syntax = in && size_t(in.tellg()) == size && newline == '\n';
294 connection.receive_pong(timestamp);
298 logger.error("Bad syntax in input message '%1'",
299 StringData(data, size));
300 connection.handle_protocol_error(Error::bad_syntax); // Throws
304 // parse_message_received takes a (WebSocket) message and parses it.
305 // The result of the parsing is handled by an object of type Connection.
306 // Typically, Connection would be the Connection class from client.cpp
307 template <typename Connection>
308 void parse_message_received(Connection& connection, const char* data, size_t size)
310 util::MemoryInputStream in;
311 in.set_buffer(data, data + size);
312 in.unsetf(std::ios_base::skipws);
313 size_t header_size = 0;
314 std::string message_type;
317 if (message_type == "download") {
318 session_ident_type session_ident;
319 SyncProgress progress;
320 int is_body_compressed;
321 size_t uncompressed_body_size, compressed_body_size;
322 char sp_1, sp_2, sp_3, sp_4, sp_5, sp_6, sp_7, sp_8, sp_9, sp_10, newline;
324 in >> sp_1 >> session_ident >> sp_2 >> progress.scan_server_version >> sp_3 >>
325 progress.scan_client_version >> sp_4 >> progress.latest_server_version >>
326 sp_5 >> progress.latest_server_session_ident >> sp_6 >>
327 progress.latest_client_version >> sp_7 >> progress.downloadable_bytes >>
328 sp_8 >> is_body_compressed >> sp_9 >> uncompressed_body_size >> sp_10 >>
329 compressed_body_size >> newline; // Throws
331 bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' &&
332 sp_3 == ' ' && sp_4 == ' ' && sp_5 == ' ' && sp_6 == ' ' &&
333 sp_7 == ' ' && sp_8 == ' ' && sp_9 == ' ' && sp_10 == ' ' &&
337 header_size = size_t(in.tellg());
338 if (uncompressed_body_size > s_max_body_size)
339 goto limits_exceeded;
341 size_t body_size = is_body_compressed ? compressed_body_size : uncompressed_body_size;
342 if (header_size + body_size != size)
345 BinaryData body(data + header_size, body_size);
346 BinaryData uncompressed_body;
348 std::unique_ptr<char[]> uncompressed_body_buffer;
349 // if is_body_compressed == true, we must decompress the received body.
350 if (is_body_compressed) {
351 uncompressed_body_buffer.reset(new char[uncompressed_body_size]);
352 std::error_code ec = util::compression::decompress(body.data(), compressed_body_size,
353 uncompressed_body_buffer.get(),
354 uncompressed_body_size);
357 logger.error("compression::inflate: %1", ec.message());
358 connection.handle_protocol_error(Error::bad_decompression);
362 uncompressed_body = BinaryData(uncompressed_body_buffer.get(), uncompressed_body_size);
365 uncompressed_body = body;
368 logger.debug("Download message compression: is_body_compressed = %1, "
369 "compressed_body_size=%2, uncompressed_body_size=%3",
370 is_body_compressed, compressed_body_size, uncompressed_body_size);
372 util::MemoryInputStream in;
373 in.unsetf(std::ios_base::skipws);
374 in.set_buffer(uncompressed_body.data(), uncompressed_body.data() + uncompressed_body_size);
376 ReceivedChangesets received_changesets;
378 // Loop through the body and find the changesets.
380 while (position < uncompressed_body_size) {
381 version_type server_version;
382 version_type client_version;
383 timestamp_type origin_timestamp;
384 file_ident_type origin_client_file_ident;
385 size_t original_changeset_size, changeset_size;
386 char sp_1, sp_2, sp_3, sp_4, sp_5, sp_6;
388 in >> server_version >> sp_1 >> client_version >> sp_2 >> origin_timestamp >>
389 sp_3 >> origin_client_file_ident >> sp_4 >> original_changeset_size >>
390 sp_5 >> changeset_size >> sp_6;
392 bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' &&
393 sp_3 == ' ' && sp_4 == ' ' && sp_5 == ' ' && sp_6 == ' ';
396 logger.error("Bad changeset header syntax");
397 connection.handle_protocol_error(Error::bad_changeset_header_syntax);
401 // Update position to the end of the change set
402 position = size_t(in.tellg()) + changeset_size;
404 if (position > uncompressed_body_size) {
405 logger.error("Bad changeset size");
406 connection.handle_protocol_error(Error::bad_changeset_size);
410 if (server_version == 0) {
411 // The received changeset can never have version 0.
412 logger.error("Bad server version");
413 connection.handle_protocol_error(Error::bad_server_version);
417 BinaryData changeset_data(uncompressed_body.data() + size_t(in.tellg()), changeset_size);
420 if (logger.would_log(util::Logger::Level::trace)) {
421 logger.trace("Received: DOWNLOAD CHANGESET(server_version=%1, client_version=%2, "
422 "origin_timestamp=%3, origin_client_file_ident=%4, original_changeset_size=%5, changeset_size=%6)",
423 server_version, client_version, origin_timestamp,
424 origin_client_file_ident, original_changeset_size, changeset_size); // Throws
425 logger.trace("Changeset: %1",
426 _impl::clamped_hex_dump(changeset_data)); // Throws
429 Transformer::RemoteChangeset changeset_2(server_version, client_version,
430 changeset_data, origin_timestamp,
431 origin_client_file_ident);
432 changeset_2.original_changeset_size = original_changeset_size;
433 received_changesets.push_back(changeset_2); // Throws
436 connection.receive_download_message(session_ident, progress, received_changesets); // Throws
439 if (message_type == "unbound") {
440 session_ident_type session_ident;
442 in >> sp_1 >> session_ident >> newline; // Throws
443 bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' &&
447 header_size = size_t(in.tellg());
449 connection.receive_unbound_message(session_ident); // Throws
452 if (message_type == "error") {
456 session_ident_type session_ident;
457 char sp_1, sp_2, sp_3, sp_4, newline;
458 in >> sp_1 >> error_code >> sp_2 >> message_size >> sp_3 >> try_again >> sp_4 >>
459 session_ident >> newline; // Throws
460 bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && sp_3 == ' ' &&
461 sp_4 == ' ' && newline == '\n';
464 header_size = size_t(in.tellg());
465 if (header_size + message_size != size)
468 bool unknown_error = !get_protocol_error_message(error_code);
470 logger.error("Bad error code"); // Throws
471 connection.handle_protocol_error(Error::bad_error_code);
475 std::string message{data + header_size, message_size}; // Throws (copy)
477 connection.receive_error_message(error_code, message_size, try_again, session_ident, message); // Throws
480 if (message_type == "mark") {
481 session_ident_type session_ident;
482 request_ident_type request_ident;
483 char sp_1, sp_2, newline;
484 in >> sp_1 >> session_ident >> sp_2 >> request_ident >> newline; // Throws
485 bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' &&
486 sp_2 == ' ' && newline == '\n';
489 header_size = size_t(in.tellg());
491 connection.receive_mark_message(session_ident, request_ident); // Throws
494 if (message_type == "alloc") {
495 session_ident_type session_ident;
496 file_ident_type server_file_ident, client_file_ident;
497 int_fast64_t client_file_ident_secret;
498 char sp_1, sp_2, sp_3, sp_4, newline;
499 in >> sp_1 >> session_ident >> sp_2 >> server_file_ident >> sp_3 >>
500 client_file_ident >> sp_4 >> client_file_ident_secret >> newline; // Throws
501 bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' &&
502 sp_2 == ' ' && sp_3 == ' ' && sp_4 == ' ' && newline == '\n';
505 header_size = size_t(in.tellg());
507 connection.receive_alloc_message(session_ident,server_file_ident, client_file_ident,
508 client_file_ident_secret); // Throws
512 logger.error("Unknown input message type '%1'",
513 StringData(data, size));
514 connection.handle_protocol_error(Error::unknown_message);
517 logger.error("Bad syntax in input message '%1'",
518 StringData(data, size));
519 connection.handle_protocol_error(Error::bad_syntax);
522 logger.error("Limits exceeded in input message '%1'",
523 StringData(data, header_size));
524 connection.handle_protocol_error(Error::limits_exceeded);
529 static constexpr size_t s_max_body_size = std::numeric_limits<size_t>::max();
531 // Permanent buffer to use for building messages.
532 OutputBuffer m_output_buffer;
534 // Permanent buffers to use for internal purposes such as compression.
535 std::vector<char> m_buffer;
537 util::compression::CompressMemoryArena m_compress_memory_arena;
541 class ServerProtocol {
543 util::Logger& logger;
546 unknown_message = 101, // Unknown type of input message
547 bad_syntax = 102, // Bad syntax in input message head
548 limits_exceeded = 103, // Limits exceeded in input message
549 bad_decompression = 104, // Error in decompression (UPLOAD)
550 bad_changeset_header_syntax = 105, // Bad syntax in changeset header (UPLOAD)
551 bad_changeset_size = 106, // Changeset size doesn't fit in message (UPLOAD)
554 ServerProtocol(util::Logger& logger);
556 // Messages sent by the server to the client
558 void make_alloc_message(OutputBuffer& out, session_ident_type session_ident,
559 file_ident_type server_file_ident,
560 file_ident_type client_file_ident,
561 std::int_fast64_t client_file_ident_secret);
563 void make_unbound_message(OutputBuffer& out, session_ident_type session_ident);
566 struct ChangesetInfo {
567 version_type server_version;
568 version_type client_version;
570 size_t original_size;
573 void make_download_message(int protocol_version, OutputBuffer& out, session_ident_type session_ident,
574 version_type scan_server_version,
575 version_type scan_client_version,
576 version_type latest_server_version,
577 int_fast64_t latest_server_session_ident,
578 version_type latest_client_version,
579 uint_fast64_t downloadable_bytes,
580 std::size_t num_changesets, BinaryData body);
582 void make_error_message(OutputBuffer& out, ProtocolError error_code,
583 const char* message, size_t message_size,
584 bool try_again, session_ident_type session_ident);
586 void make_mark_message(OutputBuffer& out, session_ident_type session_ident,
587 request_ident_type request_ident);
589 void make_pong(OutputBuffer& out, uint_fast64_t timestamp);
591 // Messages received by the server.
593 // parse_ping_received takes a (WebSocket) ping and parses it.
594 // The result of the parsing is handled by an object of type Connection.
595 // Typically, Connection would be the Connection class from server.cpp
596 template <typename Connection>
597 void parse_ping_received(Connection& connection, const char* data, size_t size)
599 util::MemoryInputStream in;
600 in.set_buffer(data, data + size);
601 in.unsetf(std::ios_base::skipws);
603 int_fast64_t timestamp, rtt;
606 in >> timestamp >> sp_1 >> rtt >> newline;
607 bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' &&
612 connection.receive_ping(timestamp, rtt);
616 logger.error("Bad syntax in PING message '%1'",
617 StringData(data, size));
618 connection.handle_protocol_error(Error::bad_syntax);
622 // UploadChangeset is used to store received changesets in
623 // the UPLOAD message.
624 struct UploadChangeset {
625 version_type client_version;
626 version_type server_version;
627 timestamp_type timestamp;
628 BinaryData changeset;
631 // parse_message_received takes a (WebSocket) message and parses it.
632 // The result of the parsing is handled by an object of type Connection.
633 // Typically, Connection would be the Connection class from server.cpp
634 template <typename Connection>
635 void parse_message_received(Connection& connection, const char* data, size_t size)
637 util::MemoryInputStream in;
638 in.set_buffer(data, data + size);
639 in.unsetf(std::ios_base::skipws);
640 size_t header_size = 0;
641 std::string message_type;
644 if (message_type == "upload") {
645 session_ident_type session_ident;
646 int is_body_compressed;
647 size_t uncompressed_body_size, compressed_body_size;
648 char sp_1, sp_2, sp_3, sp_4, newline;
649 in >> sp_1 >> session_ident >> sp_2 >> is_body_compressed >> sp_3 >>
650 uncompressed_body_size >> sp_4 >> compressed_body_size >>
652 bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && sp_3 == ' ' &&
653 sp_4 == ' ' && newline == '\n';
657 header_size = size_t(in.tellg());
658 if (uncompressed_body_size > s_max_body_size)
659 goto limits_exceeded;
661 size_t body_size = is_body_compressed ? compressed_body_size : uncompressed_body_size;
662 if (header_size + body_size != size)
665 BinaryData body(data + header_size, body_size);
667 BinaryData uncompressed_body;
668 std::unique_ptr<char[]> uncompressed_body_buffer;
669 // if is_body_compressed == true, we must decompress the received body.
670 if (is_body_compressed) {
671 uncompressed_body_buffer.reset(new char[uncompressed_body_size]);
672 std::error_code ec = util::compression::decompress(body.data(), compressed_body_size,
673 uncompressed_body_buffer.get(),
674 uncompressed_body_size);
677 logger.error("compression::inflate: %1", ec.message());
678 connection.handle_protocol_error(Error::bad_decompression);
682 uncompressed_body = BinaryData(uncompressed_body_buffer.get(), uncompressed_body_size);
685 uncompressed_body = body;
688 logger.debug("Upload message compression: is_body_compressed = %1, "
689 "compressed_body_size=%2, uncompressed_body_size=%3",
690 is_body_compressed, compressed_body_size, uncompressed_body_size);
692 util::MemoryInputStream in;
693 in.unsetf(std::ios_base::skipws);
694 in.set_buffer(uncompressed_body.data(), uncompressed_body.data() + uncompressed_body_size);
696 std::vector<UploadChangeset> upload_changesets;
698 // Loop through the body and find the changesets.
700 while (position < uncompressed_body_size) {
701 version_type client_version;
702 version_type server_version;
703 timestamp_type timestamp;
704 size_t changeset_size;
705 char sp_1, sp_2, sp_3, sp_4;
707 in >> client_version >> sp_1 >> server_version >> sp_2 >> timestamp >>
708 sp_3 >> changeset_size >> sp_4;
710 bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' &&
711 sp_3 == ' ' && sp_4 == ' ';
714 logger.error("Bad changeset header syntax");
715 connection.handle_protocol_error(Error::bad_changeset_header_syntax);
719 // Update position to the end of the change set
720 position = size_t(in.tellg()) + changeset_size;
722 if (position > uncompressed_body_size) {
723 logger.error("Bad changeset size");
724 connection.handle_protocol_error(Error::bad_changeset_size);
728 BinaryData changeset_data(uncompressed_body.data() +
729 size_t(in.tellg()), changeset_size);
732 if (logger.would_log(util::Logger::Level::trace)) {
734 "Received: UPLOAD CHANGESET(client_version=%1, "
735 "server_version=%2, timestamp=%3, changeset_size=%4)",
736 client_version, server_version, timestamp,
737 changeset_size); // Throws
738 logger.trace("Changeset: %1",
739 _impl::clamped_hex_dump(changeset_data)); // Throws
742 UploadChangeset upload_changeset {
749 upload_changesets.push_back(upload_changeset); // Throws
752 connection.receive_upload_message(session_ident,upload_changesets); // Throws
755 if (message_type == "mark") {
756 session_ident_type session_ident;
757 request_ident_type request_ident;
758 char sp_1, sp_2, newline;
759 in >> sp_1 >> session_ident >> sp_2 >> request_ident >> newline;
760 bool good_syntax = in && size_t(in.tellg()) == size &&
761 sp_1 == ' ' && sp_2 == ' ' && newline == '\n';
766 connection.receive_mark_message(session_ident, request_ident); // Throws
769 if (message_type == "bind") {
770 session_ident_type session_ident;
772 size_t signed_user_token_size;
773 bool need_file_ident_pair;
774 char sp_1, sp_2, sp_3, sp_4, newline;
775 in >> sp_1 >> session_ident >> sp_2 >> path_size >> sp_3 >>
776 signed_user_token_size >> sp_4 >> need_file_ident_pair >>
778 bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' &&
779 sp_3 == ' ' && sp_4 == ' ' && newline == '\n';
782 header_size = size_t(in.tellg());
785 if (path_size > s_max_path_size)
786 goto limits_exceeded;
787 if (signed_user_token_size > s_max_signed_user_token_size)
788 goto limits_exceeded;
789 if (header_size + path_size + signed_user_token_size != size)
792 std::string path {data + header_size, path_size}; // Throws
793 std::string signed_user_token {data + header_size + path_size,
794 signed_user_token_size}; // Throws
796 connection.receive_bind_message(session_ident, std::move(path),
797 std::move(signed_user_token),
798 need_file_ident_pair); // Throws
801 if (message_type == "refresh") {
802 session_ident_type session_ident;
803 size_t signed_user_token_size;
804 char sp_1, sp_2, newline;
805 in >> sp_1 >> session_ident >> sp_2 >> signed_user_token_size >>
807 bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && newline == '\n';
810 header_size = size_t(in.tellg());
811 if (signed_user_token_size > s_max_signed_user_token_size)
812 goto limits_exceeded;
813 if (header_size + signed_user_token_size != size)
816 std::string signed_user_token {data + header_size, signed_user_token_size};
818 connection.receive_refresh_message(session_ident, std::move(signed_user_token)); // Throws
821 if (message_type == "ident") {
822 session_ident_type session_ident;
823 file_ident_type server_file_ident, client_file_ident;
824 int_fast64_t client_file_ident_secret;
825 version_type scan_server_version, scan_client_version, latest_server_version;
826 int_fast64_t latest_server_session_ident;
827 char sp_1, sp_2, sp_3, sp_4, sp_5, sp_6, sp_7, sp_8, newline;
828 in >> sp_1 >> session_ident >> sp_2 >> server_file_ident >> sp_3 >>
829 client_file_ident >> sp_4 >> client_file_ident_secret >> sp_5 >>
830 scan_server_version >> sp_6 >> scan_client_version >> sp_7 >>
831 latest_server_version >> sp_8 >> latest_server_session_ident >>
833 bool good_syntax = in && size_t(in.tellg()) == size && sp_1 == ' ' &&
834 sp_2 == ' ' && sp_3 == ' ' && sp_4 == ' ' && sp_5 == ' ' &&
835 sp_6 == ' ' && sp_7 == ' ' && sp_8 == ' ' && newline == '\n';
840 connection.receive_ident_message(session_ident, server_file_ident, client_file_ident,
841 client_file_ident_secret, scan_server_version,
842 scan_client_version, latest_server_version,
843 latest_server_session_ident); // Throws
846 if (message_type == "unbind") {
847 session_ident_type session_ident;
849 in >> sp_1 >> session_ident >> newline;
850 bool good_syntax = in && size_t(in.tellg()) == size &&
851 sp_1 == ' ' && newline == '\n';
856 connection.receive_unbind_message(session_ident); // Throws
859 if (message_type == "client") {
860 int_fast64_t protocol_version;
861 char sp_1, sp_2, newline;
862 size_t client_info_size;
863 in >> sp_1 >> protocol_version >> sp_2 >> client_info_size >> newline;
864 bool good_syntax = in && sp_1 == ' ' && sp_2 == ' ' && newline == '\n';
867 header_size = size_t(in.tellg());
868 bool limits_exceeded = (client_info_size > s_max_client_info_size);
870 goto limits_exceeded;
871 if (header_size + client_info_size != size)
874 std::string client_info {data + header_size, client_info_size}; // Throws
876 connection.receive_client_message(protocol_version, std::move(client_info)); // Throws
882 logger.error("Unknown input message type '%1'", StringData(data, size)); // Throws
884 logger.error("Unknown input message type '%1'.......", StringData(data, 256)); // Throws
886 connection.handle_protocol_error(Error::unknown_message);
890 logger.error("Bad syntax in input message '%1'",
891 StringData(data, size));
892 connection.handle_protocol_error(Error::bad_syntax); // Throws
895 logger.error("Limits exceeded in input message '%1'",
896 StringData(data, header_size));
897 connection.handle_protocol_error(Error::limits_exceeded); // Throws
901 void insert_single_changeset_download_message(OutputBuffer& out, const ChangesetInfo& changeset_info);
904 static constexpr size_t s_max_head_size = 256;
905 static constexpr size_t s_max_signed_user_token_size = 2048;
906 static constexpr size_t s_max_client_info_size = 1024;
907 static constexpr size_t s_max_path_size = 1024;
908 static constexpr size_t s_max_body_size = std::numeric_limits<size_t>::max();
910 util::compression::CompressMemoryArena m_compress_memory_arena;
912 // Permanent buffer to use for internal purposes such as compression.
913 std::vector<char> m_buffer;
915 // Outputbuffer to use for internal purposes such as creating the
917 OutputBuffer m_output_buffer;
920 // make_authorization_header() makes the value of the Authorization header used in the
921 // sync Websocket handshake.
922 std::string make_authorization_header(const std::string& signed_user_token);
924 // parse_authorization_header() parses the value of the Authorization header and returns
925 // the signed_user_token. None is returned in case of syntax error.
926 util::Optional<StringData> parse_authorization_header(const std::string& authorization_header);
928 } // namespace protocol
932 #endif // REALM_SYNC_PROTOCOL_HPP