| // Copyright 2016 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef COMPONENTS_SYNC_ENGINE_LOOPBACK_SERVER_LOOPBACK_SERVER_H_ | |
| #define COMPONENTS_SYNC_ENGINE_LOOPBACK_SERVER_LOOPBACK_SERVER_H_ | |
| #include <stdint.h> | |
| #include <map> | |
| #include <memory> | |
| #include <optional> | |
| #include <string> | |
| #include <vector> | |
| #include "base/files/file_path.h" | |
| #include "base/files/important_file_writer.h" | |
| #include "base/functional/callback.h" | |
| #include "base/memory/raw_ptr.h" | |
| #include "base/sequence_checker.h" | |
| #include "base/values.h" | |
| #include "components/sync/base/data_type.h" | |
| #include "components/sync/engine/loopback_server/loopback_server_entity.h" | |
| #include "components/sync/protocol/sync.pb.h" | |
| #include "net/http/http_status_code.h" | |
| namespace sync_pb { | |
| class DeletionOrigin; | |
| class LoopbackServerProto; | |
| class EntitySpecifics; | |
| class SyncEntity; | |
| } // namespace sync_pb | |
| namespace fake_server { | |
| class FakeServer; | |
| } | |
| namespace syncer { | |
| // A loopback version of the Sync server used for local profile serialization. | |
| class LoopbackServer : public base::ImportantFileWriter::DataSerializer { | |
| public: | |
| class ObserverForTests { | |
| public: | |
| virtual ~ObserverForTests() = default; | |
| // Called after the server has processed a successful commit. The types | |
| // updated as part of the commit are passed in `committed_data_types`. | |
| virtual void OnCommit(syncer::DataTypeSet committed_data_types) = 0; | |
| // Called when a committed tombstone includes a deletion origin. | |
| virtual void OnCommittedDeletionOrigin( | |
| syncer::DataType type, | |
| const sync_pb::DeletionOrigin& deletion_origin) = 0; | |
| }; | |
| explicit LoopbackServer(const base::FilePath& persistent_file); | |
| ~LoopbackServer() override; | |
| // Handles a /command POST (with the given `message`) to the server. | |
| // `response` must not be null. | |
| net::HttpStatusCode HandleCommand( | |
| const sync_pb::ClientToServerMessage& message, | |
| sync_pb::ClientToServerResponse* response); | |
| // Enables strong consistency model (i.e. server detects conflicts). | |
| void EnableStrongConsistencyWithConflictDetectionModel(); | |
| enum class UpdateMode { | |
| kIncremental, | |
| kFull, | |
| }; | |
| // Configures the update mode for `data_type`. When set to | |
| // `UpdateMode::kFull`, the server will respond with a full update and a GC | |
| // directive whenever there are new or updated entities for `data_type`. | |
| void SetUpdateMode(DataType data_type, UpdateMode update_mode); | |
| // Sets a maximum batch size for GetUpdates requests. | |
| void SetMaxGetUpdatesBatchSize(int batch_size) { | |
| max_get_updates_batch_size_ = batch_size; | |
| } | |
| void SetBagOfChipsForTesting(const sync_pb::ChipBag& bag_of_chips) { | |
| bag_of_chips_ = bag_of_chips; | |
| } | |
| void TriggerMigrationForTesting(DataTypeSet data_types); | |
| // Enables using GarbageCollectionDirective (clear_metadata) instead of | |
| // MIGRATION_DONE error to trigger migration/re-sync on the client. | |
| void EnableGcDirectiveForMigration(); | |
| int GetMigrationVersion(DataType type) const; | |
| static int GetMigrationVersionFromProgressTokenForTesting( | |
| const std::string& token); | |
| const std::vector<std::vector<uint8_t>>& GetKeystoreKeysForTesting() const { | |
| return keystore_keys_; | |
| } | |
| void AddNewKeystoreKeyForTesting(); | |
| void SetThrottledTypesForTesting(DataTypeSet data_types) { | |
| throttled_types_ = data_types; | |
| } | |
| private: | |
| // Allow the FakeServer decorator to inspect the internals of this class. | |
| friend class fake_server::FakeServer; | |
| using EntityMap = | |
| std::map<std::string, std::unique_ptr<LoopbackServerEntity>>; | |
| using ResponseTypeProvider = | |
| base::RepeatingCallback<sync_pb::CommitResponse::ResponseType( | |
| const LoopbackServerEntity& entity)>; | |
| void FlushToDisk(); | |
| // ImportantFileWriter::DataSerializer: | |
| std::optional<std::string> SerializeData() override; | |
| // Gets LoopbackServer ready for syncing. | |
| void Init(); | |
| // Processes a GetUpdates call. | |
| bool HandleGetUpdatesRequest(const sync_pb::GetUpdatesMessage& get_updates, | |
| const std::string& store_birthday, | |
| const std::string& invalidator_client_id, | |
| sync_pb::GetUpdatesResponse* response, | |
| std::vector<DataType>* datatypes_to_migrate); | |
| void PopulateGcDirectiveMigrationResponse( | |
| const sync_pb::GetUpdatesMessage& get_updates, | |
| const std::vector<DataType>& datatypes_to_migrate, | |
| sync_pb::GetUpdatesResponse* response); | |
| // Processes a Commit call. | |
| bool HandleCommitRequest(const sync_pb::CommitMessage& message, | |
| const std::string& invalidator_client_id, | |
| sync_pb::CommitResponse* response, | |
| DataTypeSet* throttled_datatypes_in_request); | |
| void ClearServerData(); | |
| void DeleteAllEntitiesForDataType(DataType data_type); | |
| // Creates and saves a permanent folder for Bookmarks (e.g., Bookmark Bar). | |
| bool CreatePermanentBookmarkFolder(const std::string& server_tag, | |
| const std::string& name); | |
| // Returns a pointer to the permanent bookmark folder with `server_tag` if it | |
| // exists, or nullptr otherwise. | |
| const LoopbackServerEntity* FindPermanentBookmarkFolder( | |
| const std::string& server_tag) const; | |
| // Inserts the default permanent items in `entities_`. | |
| bool CreateDefaultPermanentItems(); | |
| // Returns generated key which may contain any bytes (not necessarily UTF-8). | |
| std::vector<uint8_t> GenerateNewKeystoreKey() const; | |
| // Saves a `entity` to `entities_`. | |
| void SaveEntity(std::unique_ptr<LoopbackServerEntity> entity); | |
| // Commits a client-side SyncEntity to the server as a LoopbackServerEntity. | |
| // The client that sent the commit is identified via `client_guid`. The | |
| // parent ID string present in `client_entity` should be ignored in favor | |
| // of `parent_id`. If the commit is successful, the entity's server ID string | |
| // is returned and a new LoopbackServerEntity is saved in `entities_`. | |
| std::string CommitEntity( | |
| const sync_pb::SyncEntity& client_entity, | |
| sync_pb::CommitResponse_EntryResponse* entry_response, | |
| const std::string& client_guid, | |
| const std::string& parent_id); | |
| // Populates `entry_response` based on the stored entity identified by | |
| // `entity_id`. It is assumed that the entity identified by `entity_id` has | |
| // already been stored using SaveEntity. | |
| void BuildEntryResponseForSuccessfulCommit( | |
| const std::string& entity_id, | |
| sync_pb::CommitResponse_EntryResponse* entry_response); | |
| // Determines whether the SyncEntity with id_string `id` is a child of an | |
| // entity with id_string `potential_parent_id`. | |
| bool IsChild(const std::string& id, const std::string& potential_parent_id); | |
| // Creates and saves tombstones for all children of the entity with the given | |
| // `parent_id`. A tombstone is not created for the entity itself. | |
| void DeleteChildren(const std::string& parent_id); | |
| // Updates the `entity` to a new version and increments the version counter | |
| // that the server uses to assign versions. | |
| void UpdateEntityVersion(LoopbackServerEntity* entity); | |
| // Returns the store birthday. | |
| std::string GetStoreBirthday() const; | |
| // Returns all entities stored by the server of the given `data_type`. | |
| // Permanent entities are excluded. This method is only used in tests. | |
| std::vector<sync_pb::SyncEntity> GetSyncEntitiesByDataType( | |
| syncer::DataType data_type); | |
| // Returns a list of permanent entities of the given `data_type`. This method | |
| // is only used in tests. | |
| std::vector<sync_pb::SyncEntity> GetPermanentSyncEntitiesByDataType( | |
| syncer::DataType data_type); | |
| // Creates a `base::DictValue` representation of all entities present in the | |
| // server. The dictionary keys are the strings generated by | |
| // DataTypeToDebugString and the values are base::ListValues containing | |
| // StringValue versions of entity names. Permanent entities are excluded. Used | |
| // by test to verify the contents of the server state. | |
| base::DictValue GetEntitiesAsDictForTesting(); | |
| // Modifies the entity on the server with the given `id`. The entity's | |
| // EntitySpecifics are replaced with `updated_specifics` and its version is | |
| // updated to n+1. If the given `id` does not exist or the DataType of | |
| // `updated_specifics` does not match the entity, false is returned. | |
| // Otherwise, true is returned to represent a successful modification. | |
| // | |
| // This method sometimes updates entity data beyond EntitySpecifics. For | |
| // example, in the case of a bookmark, changing the BookmarkSpecifics title | |
| // field will modify the top-level entity's name field. | |
| // This method is only used in tests. | |
| bool ModifyEntitySpecifics(const std::string& id, | |
| const sync_pb::EntitySpecifics& updated_specifics); | |
| // This method is only used in tests. | |
| bool ModifyBookmarkEntity(const std::string& id, | |
| const std::string& parent_id, | |
| const sync_pb::EntitySpecifics& updated_specifics); | |
| // Use this callback to generate response types for entities. They will still | |
| // be "committed" and stored as normal, this only affects the response type | |
| // the client sees. This allows tests to still inspect what the client has | |
| // done, although not as useful of a mechanism for multi client tests. Care | |
| // should be taken when failing responses, as the client will go into | |
| // exponential backoff, which can cause tests to be slow or time out. | |
| // This method is only used in tests. | |
| void OverrideResponseType(ResponseTypeProvider response_type_override); | |
| // Serializes the server state to `proto`. | |
| void SerializeState(sync_pb::LoopbackServerProto* proto) const; | |
| // Populates the server state from `proto`. Returns true iff successful. | |
| bool DeSerializeState(const sync_pb::LoopbackServerProto& proto); | |
| // Schedules committing state to disk at some later time. Repeat calls are | |
| // batched together. Outstanding scheduled writes are committed at shutdown. | |
| // Returns true on success. | |
| bool ScheduleSaveStateToFile(); | |
| // Loads all entities and server state from a protobuf file. Returns true on | |
| // success. | |
| bool LoadStateFromFile(); | |
| void set_observer_for_tests(ObserverForTests* observer) { | |
| observer_for_tests_ = observer; | |
| } | |
| bool strong_consistency_model_enabled_ = false; | |
| // This is the last version number assigned to an entity. The next entity will | |
| // have a version number of version_ + 1. | |
| int64_t version_ = 0; | |
| int64_t store_birthday_ = 0; | |
| DataTypeSet throttled_types_; | |
| DataTypeSet full_update_types_; | |
| std::optional<sync_pb::ChipBag> bag_of_chips_; | |
| std::map<DataType, int> migration_versions_; | |
| bool use_gc_directive_for_migration_ = false; | |
| int max_get_updates_batch_size_ = 1000000; | |
| EntityMap entities_; | |
| std::vector<std::vector<uint8_t>> keystore_keys_; | |
| // The file used to store the local sync data. | |
| const base::FilePath persistent_file_; | |
| // Used to limit the rate of file rewrites due to updates. | |
| base::ImportantFileWriter writer_; | |
| // Used to verify that LoopbackServer is only used from one sequence. | |
| SEQUENCE_CHECKER(sequence_checker_); | |
| // Used to observe the completion of commit messages for the sake of testing. | |
| raw_ptr<ObserverForTests> observer_for_tests_ = nullptr; | |
| // Response type override callback used in tests. | |
| ResponseTypeProvider response_type_override_; | |
| }; | |
| } // namespace syncer | |
| #endif // COMPONENTS_SYNC_ENGINE_LOOPBACK_SERVER_LOOPBACK_SERVER_H_ |