Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(566)

Side by Side Diff: components/sync/engine_impl/loopback_server/loopback_server.cc

Issue 2360703002: [Sync] Implements the loopback sync server. (Closed)
Patch Set: Address comment. Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/sync/test/fake_server/fake_server.h" 5 #include "components/sync/engine_impl/loopback_server/loopback_server.h"
6
7 #include <stdint.h>
6 8
7 #include <algorithm> 9 #include <algorithm>
8 #include <limits> 10 #include <limits>
11 #include <memory>
9 #include <set> 12 #include <set>
13 #include <string>
10 #include <utility> 14 #include <utility>
15 #include <vector>
11 16
17 #include "base/files/file_util.h"
12 #include "base/guid.h" 18 #include "base/guid.h"
13 #include "base/logging.h" 19 #include "base/logging.h"
20 #include "base/rand_util.h"
14 #include "base/stl_util.h" 21 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h" 22 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h" 23 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h" 24 #include "base/strings/stringprintf.h"
18 #include "base/synchronization/lock.h" 25 #include "base/synchronization/lock.h"
19 #include "components/sync/test/fake_server/bookmark_entity.h" 26 #include "components/sync/base/model_type.h"
20 #include "components/sync/test/fake_server/permanent_entity.h" 27 #include "components/sync/engine_impl/loopback_server/persistent_bookmark_entity .h"
21 #include "components/sync/test/fake_server/tombstone_entity.h" 28 #include "components/sync/engine_impl/loopback_server/persistent_permanent_entit y.h"
22 #include "components/sync/test/fake_server/unique_client_entity.h" 29 #include "components/sync/engine_impl/loopback_server/persistent_tombstone_entit y.h"
30 #include "components/sync/engine_impl/loopback_server/persistent_unique_client_e ntity.h"
23 #include "net/base/net_errors.h" 31 #include "net/base/net_errors.h"
24 #include "net/http/http_status_code.h" 32 #include "net/http/http_status_code.h"
25 33
26 using std::string; 34 using std::string;
27 using std::vector; 35 using std::vector;
36
28 using syncer::GetModelType; 37 using syncer::GetModelType;
29 using syncer::GetModelTypeFromSpecifics; 38 using syncer::GetModelTypeFromSpecifics;
30 using syncer::ModelType; 39 using syncer::ModelType;
31 using syncer::ModelTypeSet; 40 using syncer::ModelTypeSet;
32 41
33 namespace fake_server { 42 namespace syncer {
34 43
35 class FakeServerEntity; 44 class LoopbackServerEntity;
36 45
37 namespace { 46 namespace {
38 47
39 // The default keystore key. 48 static const int kCurrentLoopbackServerProtoVersion = 1;
40 static const char kDefaultKeystoreKey[] = "1111111111111111"; 49 static const int kKeystoreKeyLenght = 16;
41 50
42 // Properties of the bookmark bar permanent folder. 51 // Properties of the bookmark bar permanent folders.
43 static const char kBookmarkBarFolderServerTag[] = "bookmark_bar"; 52 static const char kBookmarkBarFolderServerTag[] = "bookmark_bar";
44 static const char kBookmarkBarFolderName[] = "Bookmark Bar"; 53 static const char kBookmarkBarFolderName[] = "Bookmark Bar";
45
46 // Properties of the other bookmarks permanent folder.
47 static const char kOtherBookmarksFolderServerTag[] = "other_bookmarks"; 54 static const char kOtherBookmarksFolderServerTag[] = "other_bookmarks";
48 static const char kOtherBookmarksFolderName[] = "Other Bookmarks"; 55 static const char kOtherBookmarksFolderName[] = "Other Bookmarks";
49
50 // Properties of the synced bookmarks permanent folder.
51 static const char kSyncedBookmarksFolderServerTag[] = "synced_bookmarks"; 56 static const char kSyncedBookmarksFolderServerTag[] = "synced_bookmarks";
52 static const char kSyncedBookmarksFolderName[] = "Synced Bookmarks"; 57 static const char kSyncedBookmarksFolderName[] = "Synced Bookmarks";
53 58
54 // A filter used during GetUpdates calls to determine what information to 59 // A filter used during GetUpdates calls to determine what information to
55 // send back to the client. There is a 1:1 correspondence between any given 60 // send back to the client. There is a 1:1 correspondence between any given
56 // GetUpdates call and an UpdateSieve instance. 61 // GetUpdates call and an UpdateSieve instance.
57 class UpdateSieve { 62 class UpdateSieve {
58 public: 63 public:
59 ~UpdateSieve() {} 64 ~UpdateSieve() {}
60 65
(...skipping 15 matching lines...) Expand all
76 new_marker->set_data_type_id( 81 new_marker->set_data_type_id(
77 GetSpecificsFieldNumberFromModelType(it->first)); 82 GetSpecificsFieldNumberFromModelType(it->first));
78 83
79 int64_t version = std::max(new_version, it->second); 84 int64_t version = std::max(new_version, it->second);
80 new_marker->set_token(base::Int64ToString(version)); 85 new_marker->set_token(base::Int64ToString(version));
81 } 86 }
82 } 87 }
83 88
84 // Determines whether the server should send an |entity| to the client as 89 // Determines whether the server should send an |entity| to the client as
85 // part of a GetUpdatesResponse. 90 // part of a GetUpdatesResponse.
86 bool ClientWantsItem(const FakeServerEntity& entity) const { 91 bool ClientWantsItem(const LoopbackServerEntity& entity) const {
87 int64_t version = entity.GetVersion(); 92 int64_t version = entity.GetVersion();
88 if (version <= min_version_) { 93 if (version <= min_version_) {
89 return false; 94 return false;
90 } else if (entity.IsDeleted()) { 95 } else if (entity.IsDeleted()) {
91 return true; 96 return true;
92 } 97 }
93 98
94 ModelTypeToVersionMap::const_iterator it = 99 ModelTypeToVersionMap::const_iterator it =
95 request_from_version_.find(entity.model_type()); 100 request_from_version_.find(entity.GetModelType());
96 101
97 return it == request_from_version_.end() ? false : it->second < version; 102 return it == request_from_version_.end() ? false : it->second < version;
98 } 103 }
99 104
100 // Returns the minimum version seen across all types. 105 // Returns the minimum version seen across all types.
101 int64_t GetMinVersion() const { return min_version_; } 106 int64_t GetMinVersion() const { return min_version_; }
102 107
103 private: 108 private:
104 typedef std::map<ModelType, int64_t> ModelTypeToVersionMap; 109 typedef std::map<ModelType, int64_t> ModelTypeToVersionMap;
105 110
(...skipping 21 matching lines...) Expand all
127 sync_pb::DataTypeProgressMarker marker = 132 sync_pb::DataTypeProgressMarker marker =
128 get_updates_message.from_progress_marker(i); 133 get_updates_message.from_progress_marker(i);
129 134
130 int64_t version = 0; 135 int64_t version = 0;
131 // Let the version remain zero if there is no token or an empty token (the 136 // Let the version remain zero if there is no token or an empty token (the
132 // first request for this type). 137 // first request for this type).
133 if (marker.has_token() && !marker.token().empty()) { 138 if (marker.has_token() && !marker.token().empty()) {
134 bool parsed = base::StringToInt64(marker.token(), &version); 139 bool parsed = base::StringToInt64(marker.token(), &version);
135 CHECK(parsed) << "Unable to parse progress marker token."; 140 CHECK(parsed) << "Unable to parse progress marker token.";
136 } 141 }
137
138 ModelType model_type = 142 ModelType model_type =
139 syncer::GetModelTypeFromSpecificsFieldNumber(marker.data_type_id()); 143 syncer::GetModelTypeFromSpecificsFieldNumber(marker.data_type_id());
140 request_from_version[model_type] = version; 144 request_from_version[model_type] = version;
141 145
142 if (version < min_version) 146 if (version < min_version)
143 min_version = version; 147 min_version = version;
144 } 148 }
145 149
146 return std::unique_ptr<UpdateSieve>( 150 return std::unique_ptr<UpdateSieve>(
147 new UpdateSieve(request_from_version, min_version)); 151 new UpdateSieve(request_from_version, min_version));
148 } 152 }
149 153
150 // Returns whether |entity| is deleted or permanent.
151 bool IsDeletedOrPermanent(const FakeServerEntity& entity) {
152 return entity.IsDeleted() || entity.IsPermanent();
153 }
154
155 } // namespace 154 } // namespace
156 155
157 FakeServer::FakeServer() 156 LoopbackServer::LoopbackServer(const base::FilePath& persistent_file)
158 : version_(0), 157 : version_(0), store_birthday_(0), persistent_file_(persistent_file) {
159 store_birthday_(0),
160 authenticated_(true),
161 error_type_(sync_pb::SyncEnums::SUCCESS),
162 alternate_triggered_errors_(false),
163 request_counter_(0),
164 network_enabled_(true),
165 weak_ptr_factory_(this) {
166 Init(); 158 Init();
167 } 159 }
168 160
169 FakeServer::~FakeServer() {} 161 LoopbackServer::~LoopbackServer() {}
170 162
171 void FakeServer::Init() { 163 void LoopbackServer::Init() {
172 keystore_keys_.push_back(kDefaultKeystoreKey); 164 if (LoadStateFromFile(persistent_file_))
165 return;
166
167 keystore_keys_.push_back(GenerateNewKeystoreKey());
173 168
174 const bool create_result = CreateDefaultPermanentItems(); 169 const bool create_result = CreateDefaultPermanentItems();
175 DCHECK(create_result) << "Permanent items were not created successfully."; 170 DCHECK(create_result) << "Permanent items were not created successfully.";
176 } 171 }
177 172
178 bool FakeServer::CreatePermanentBookmarkFolder(const std::string& server_tag, 173 std::string LoopbackServer::GenerateNewKeystoreKey() const {
179 const std::string& name) { 174 // TODO(pastarmovj): Check if true random bytes is ok or alpha-nums is needed?
175 return base::RandBytesAsString(kKeystoreKeyLenght);
176 }
177
178 bool LoopbackServer::CreatePermanentBookmarkFolder(
179 const std::string& server_tag,
180 const std::string& name) {
180 DCHECK(thread_checker_.CalledOnValidThread()); 181 DCHECK(thread_checker_.CalledOnValidThread());
181 std::unique_ptr<FakeServerEntity> entity = 182 std::unique_ptr<LoopbackServerEntity> entity =
182 PermanentEntity::Create(syncer::BOOKMARKS, server_tag, name, 183 PersistentPermanentEntity::Create(syncer::BOOKMARKS, server_tag, name,
183 ModelTypeToRootTag(syncer::BOOKMARKS)); 184 ModelTypeToRootTag(syncer::BOOKMARKS));
184 if (!entity) 185 if (!entity)
185 return false; 186 return false;
186 187
187 SaveEntity(std::move(entity)); 188 SaveEntity(std::move(entity));
188 return true; 189 return true;
189 } 190 }
190 191
191 bool FakeServer::CreateDefaultPermanentItems() { 192 bool LoopbackServer::CreateDefaultPermanentItems() {
192 // Permanent folders are always required for Bookmarks (hierarchical 193 // Permanent folders are always required for Bookmarks (hierarchical
193 // structure) and Nigori (data stored in permanent root folder). 194 // structure) and Nigori (data stored in permanent root folder).
194 ModelTypeSet permanent_folder_types = 195 ModelTypeSet permanent_folder_types =
195 ModelTypeSet(syncer::BOOKMARKS, syncer::NIGORI); 196 ModelTypeSet(syncer::BOOKMARKS, syncer::NIGORI);
196 197
197 for (ModelTypeSet::Iterator it = permanent_folder_types.First(); it.Good(); 198 for (ModelTypeSet::Iterator it = permanent_folder_types.First(); it.Good();
198 it.Inc()) { 199 it.Inc()) {
199 ModelType model_type = it.Get(); 200 ModelType model_type = it.Get();
200 201
201 std::unique_ptr<FakeServerEntity> top_level_entity = 202 std::unique_ptr<LoopbackServerEntity> top_level_entity =
202 PermanentEntity::CreateTopLevel(model_type); 203 PersistentPermanentEntity::CreateTopLevel(model_type);
203 if (!top_level_entity) { 204 if (!top_level_entity) {
204 return false; 205 return false;
205 } 206 }
206 SaveEntity(std::move(top_level_entity)); 207 SaveEntity(std::move(top_level_entity));
207 208
208 if (model_type == syncer::BOOKMARKS) { 209 if (model_type == syncer::BOOKMARKS) {
209 if (!CreatePermanentBookmarkFolder(kBookmarkBarFolderServerTag, 210 if (!CreatePermanentBookmarkFolder(kBookmarkBarFolderServerTag,
210 kBookmarkBarFolderName)) 211 kBookmarkBarFolderName))
211 return false; 212 return false;
212 if (!CreatePermanentBookmarkFolder(kOtherBookmarksFolderServerTag, 213 if (!CreatePermanentBookmarkFolder(kOtherBookmarksFolderServerTag,
213 kOtherBookmarksFolderName)) 214 kOtherBookmarksFolderName))
214 return false; 215 return false;
215 } 216 }
216 } 217 }
217 218
218 return true; 219 return true;
219 } 220 }
220 221
221 void FakeServer::UpdateEntityVersion(FakeServerEntity* entity) { 222 void LoopbackServer::UpdateEntityVersion(LoopbackServerEntity* entity) {
222 entity->SetVersion(++version_); 223 entity->SetVersion(++version_);
223 } 224 }
224 225
225 void FakeServer::SaveEntity(std::unique_ptr<FakeServerEntity> entity) { 226 void LoopbackServer::SaveEntity(std::unique_ptr<LoopbackServerEntity> entity) {
226 UpdateEntityVersion(entity.get()); 227 UpdateEntityVersion(entity.get());
227 entities_[entity->id()] = std::move(entity); 228 entities_[entity->GetId()] = std::move(entity);
228 } 229 }
229 230
230 void FakeServer::HandleCommand(const string& request, 231 void LoopbackServer::HandleCommand(
231 const base::Closure& completion_closure, 232 const string& request,
232 int* error_code, 233 HttpResponse::ServerConnectionCode* server_status,
233 int* response_code, 234 int64_t* response_code,
234 std::string* response) { 235 std::string* response) {
235 DCHECK(thread_checker_.CalledOnValidThread()); 236 DCHECK(thread_checker_.CalledOnValidThread());
236 if (!network_enabled_) {
237 *error_code = net::ERR_FAILED;
238 *response_code = net::ERR_FAILED;
239 *response = string();
240 completion_closure.Run();
241 return;
242 }
243 request_counter_++;
244
245 if (!authenticated_) {
246 *error_code = 0;
247 *response_code = net::HTTP_UNAUTHORIZED;
248 *response = string();
249 completion_closure.Run();
250 return;
251 }
252 237
253 sync_pb::ClientToServerMessage message; 238 sync_pb::ClientToServerMessage message;
254 bool parsed = message.ParseFromString(request); 239 bool parsed = message.ParseFromString(request);
255 CHECK(parsed) << "Unable to parse the ClientToServerMessage."; 240 CHECK(parsed) << "Unable to parse the ClientToServerMessage.";
256 241
257 sync_pb::ClientToServerResponse response_proto; 242 sync_pb::ClientToServerResponse response_proto;
258 243
259 if (message.has_store_birthday() && 244 if (message.has_store_birthday() &&
260 message.store_birthday() != GetStoreBirthday()) { 245 message.store_birthday() != GetStoreBirthday()) {
261 response_proto.set_error_code(sync_pb::SyncEnums::NOT_MY_BIRTHDAY); 246 response_proto.set_error_code(sync_pb::SyncEnums::NOT_MY_BIRTHDAY);
262 } else if (error_type_ != sync_pb::SyncEnums::SUCCESS &&
263 ShouldSendTriggeredError()) {
264 response_proto.set_error_code(error_type_);
265 } else if (triggered_actionable_error_.get() && ShouldSendTriggeredError()) {
266 sync_pb::ClientToServerResponse_Error* error =
267 response_proto.mutable_error();
268 error->CopyFrom(*(triggered_actionable_error_.get()));
269 } else { 247 } else {
270 bool success = false; 248 bool success = false;
271 switch (message.message_contents()) { 249 switch (message.message_contents()) {
272 case sync_pb::ClientToServerMessage::GET_UPDATES: 250 case sync_pb::ClientToServerMessage::GET_UPDATES:
273 last_getupdates_message_ = message;
274 success = HandleGetUpdatesRequest(message.get_updates(), 251 success = HandleGetUpdatesRequest(message.get_updates(),
275 response_proto.mutable_get_updates()); 252 response_proto.mutable_get_updates());
276 break; 253 break;
277 case sync_pb::ClientToServerMessage::COMMIT: 254 case sync_pb::ClientToServerMessage::COMMIT:
278 last_commit_message_ = message;
279 success = HandleCommitRequest(message.commit(), 255 success = HandleCommitRequest(message.commit(),
280 message.invalidator_client_id(), 256 message.invalidator_client_id(),
281 response_proto.mutable_commit()); 257 response_proto.mutable_commit());
282 break; 258 break;
283 case sync_pb::ClientToServerMessage::CLEAR_SERVER_DATA: 259 case sync_pb::ClientToServerMessage::CLEAR_SERVER_DATA:
284 ClearServerData(); 260 ClearServerData();
285 response_proto.mutable_clear_server_data(); 261 response_proto.mutable_clear_server_data();
286 success = true; 262 success = true;
287 break; 263 break;
288 default: 264 default:
289 *error_code = net::ERR_NOT_IMPLEMENTED; 265 *server_status = HttpResponse::SYNC_SERVER_ERROR;
290 *response_code = 0; 266 *response_code = net::ERR_NOT_IMPLEMENTED;
291 *response = string(); 267 *response = string();
292 completion_closure.Run();
293 return; 268 return;
294 } 269 }
295 270
296 if (!success) { 271 if (!success) {
297 // TODO(pvalenzuela): Add logging here so that tests have more info about 272 *server_status = HttpResponse::SYNC_SERVER_ERROR;
298 // the failure. 273 *response_code = net::ERR_FAILED;
299 *error_code = net::ERR_FAILED;
300 *response_code = 0;
301 *response = string(); 274 *response = string();
302 completion_closure.Run();
303 return; 275 return;
304 } 276 }
305 277
306 response_proto.set_error_code(sync_pb::SyncEnums::SUCCESS); 278 response_proto.set_error_code(sync_pb::SyncEnums::SUCCESS);
307 } 279 }
308 280
309 response_proto.set_store_birthday(GetStoreBirthday()); 281 response_proto.set_store_birthday(GetStoreBirthday());
310 282
311 *error_code = 0; 283 *server_status = HttpResponse::SERVER_CONNECTION_OK;
312 *response_code = net::HTTP_OK; 284 *response_code = net::HTTP_OK;
313 *response = response_proto.SerializeAsString(); 285 *response = response_proto.SerializeAsString();
314 completion_closure.Run(); 286
287 // TODO(pastarmovj): This should be done asynchronously.
288 SaveStateToFile(persistent_file_);
315 } 289 }
316 290
317 bool FakeServer::GetLastCommitMessage(sync_pb::ClientToServerMessage* message) { 291 bool LoopbackServer::HandleGetUpdatesRequest(
318 if (!last_commit_message_.has_commit())
319 return false;
320
321 message->CopyFrom(last_commit_message_);
322 return true;
323 }
324
325 bool FakeServer::GetLastGetUpdatesMessage(
326 sync_pb::ClientToServerMessage* message) {
327 if (!last_getupdates_message_.has_get_updates())
328 return false;
329
330 message->CopyFrom(last_getupdates_message_);
331 return true;
332 }
333
334 bool FakeServer::HandleGetUpdatesRequest(
335 const sync_pb::GetUpdatesMessage& get_updates, 292 const sync_pb::GetUpdatesMessage& get_updates,
336 sync_pb::GetUpdatesResponse* response) { 293 sync_pb::GetUpdatesResponse* response) {
337 // TODO(pvalenzuela): Implement batching instead of sending all information 294 // TODO(pvalenzuela): Implement batching instead of sending all information
338 // at once. 295 // at once.
339 response->set_changes_remaining(0); 296 response->set_changes_remaining(0);
340 297
341 std::unique_ptr<UpdateSieve> sieve = UpdateSieve::Create(get_updates); 298 std::unique_ptr<UpdateSieve> sieve = UpdateSieve::Create(get_updates);
342 299
343 // This folder is called "Synced Bookmarks" by sync and is renamed 300 // This folder is called "Synced Bookmarks" by sync and is renamed
344 // "Mobile Bookmarks" by the mobile client UIs. 301 // "Mobile Bookmarks" by the mobile client UIs.
345 if (get_updates.create_mobile_bookmarks_folder() && 302 if (get_updates.create_mobile_bookmarks_folder() &&
346 !CreatePermanentBookmarkFolder(kSyncedBookmarksFolderServerTag, 303 !CreatePermanentBookmarkFolder(kSyncedBookmarksFolderServerTag,
347 kSyncedBookmarksFolderName)) { 304 kSyncedBookmarksFolderName)) {
348 return false; 305 return false;
349 } 306 }
350 307
351 bool send_encryption_keys_based_on_nigori = false; 308 bool send_encryption_keys_based_on_nigori = false;
352 int64_t max_response_version = 0; 309 int64_t max_response_version = 0;
353 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end(); 310 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
354 ++it) { 311 ++it) {
355 const FakeServerEntity& entity = *it->second; 312 const LoopbackServerEntity& entity = *it->second;
356 if (sieve->ClientWantsItem(entity)) { 313 if (sieve->ClientWantsItem(entity)) {
357 sync_pb::SyncEntity* response_entity = response->add_entries(); 314 sync_pb::SyncEntity* response_entity = response->add_entries();
358 entity.SerializeAsProto(response_entity); 315 entity.SerializeAsProto(response_entity);
359 316
360 max_response_version = 317 max_response_version =
361 std::max(max_response_version, response_entity->version()); 318 std::max(max_response_version, response_entity->version());
362 319
363 if (entity.model_type() == syncer::NIGORI) { 320 if (entity.GetModelType() == syncer::NIGORI) {
364 send_encryption_keys_based_on_nigori = 321 send_encryption_keys_based_on_nigori =
365 response_entity->specifics().nigori().passphrase_type() == 322 response_entity->specifics().nigori().passphrase_type() ==
366 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE; 323 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE;
367 } 324 }
368 } 325 }
369 } 326 }
370 327
371 if (send_encryption_keys_based_on_nigori || 328 if (send_encryption_keys_based_on_nigori ||
372 get_updates.need_encryption_key()) { 329 get_updates.need_encryption_key()) {
373 for (vector<string>::iterator it = keystore_keys_.begin(); 330 for (vector<string>::iterator it = keystore_keys_.begin();
374 it != keystore_keys_.end(); ++it) { 331 it != keystore_keys_.end(); ++it) {
375 response->add_encryption_keys(*it); 332 response->add_encryption_keys(*it);
376 } 333 }
377 } 334 }
378 335
379 sieve->UpdateProgressMarkers(max_response_version, response); 336 sieve->UpdateProgressMarkers(max_response_version, response);
380 return true; 337 return true;
381 } 338 }
382 339
383 string FakeServer::CommitEntity( 340 string LoopbackServer::CommitEntity(
384 const sync_pb::SyncEntity& client_entity, 341 const sync_pb::SyncEntity& client_entity,
385 sync_pb::CommitResponse_EntryResponse* entry_response, 342 sync_pb::CommitResponse_EntryResponse* entry_response,
386 const string& client_guid, 343 const string& client_guid,
387 const string& parent_id) { 344 const string& parent_id) {
388 if (client_entity.version() == 0 && client_entity.deleted()) { 345 if (client_entity.version() == 0 && client_entity.deleted()) {
389 return string(); 346 return string();
390 } 347 }
391 348
392 std::unique_ptr<FakeServerEntity> entity; 349 std::unique_ptr<LoopbackServerEntity> entity;
393 if (client_entity.deleted()) { 350 if (client_entity.deleted()) {
394 entity = TombstoneEntity::Create(client_entity.id_string(), 351 entity = PersistentTombstoneEntity::Create(client_entity);
395 client_entity.client_defined_unique_tag());
396 DeleteChildren(client_entity.id_string()); 352 DeleteChildren(client_entity.id_string());
397 } else if (GetModelType(client_entity) == syncer::NIGORI) { 353 } else if (GetModelType(client_entity) == syncer::NIGORI) {
398 // NIGORI is the only permanent item type that should be updated by the 354 // NIGORI is the only permanent item type that should be updated by the
399 // client. 355 // client.
400 EntityMap::const_iterator iter = entities_.find(client_entity.id_string()); 356 EntityMap::const_iterator iter = entities_.find(client_entity.id_string());
401 CHECK(iter != entities_.end()); 357 CHECK(iter != entities_.end());
402 entity = PermanentEntity::CreateUpdatedNigoriEntity(client_entity, 358 entity = PersistentPermanentEntity::CreateUpdatedNigoriEntity(
403 *iter->second); 359 client_entity, *iter->second);
404 } else if (client_entity.has_client_defined_unique_tag()) { 360 } else if (client_entity.has_client_defined_unique_tag()) {
405 entity = UniqueClientEntity::Create(client_entity); 361 entity = PersistentUniqueClientEntity::Create(client_entity);
406 } else { 362 } else {
407 // TODO(pvalenzuela): Validate entity's parent ID. 363 // TODO(pvalenzuela): Validate entity's parent ID.
408 EntityMap::const_iterator iter = entities_.find(client_entity.id_string()); 364 EntityMap::const_iterator iter = entities_.find(client_entity.id_string());
409 if (iter != entities_.end()) { 365 if (iter != entities_.end()) {
410 entity = BookmarkEntity::CreateUpdatedVersion(client_entity, 366 entity = PersistentBookmarkEntity::CreateUpdatedVersion(
411 *iter->second, parent_id); 367 client_entity, *iter->second, parent_id);
412 } else { 368 } else {
413 entity = BookmarkEntity::CreateNew(client_entity, parent_id, client_guid); 369 entity = PersistentBookmarkEntity::CreateNew(client_entity, parent_id,
370 client_guid);
414 } 371 }
415 } 372 }
416 373
417 if (!entity) { 374 if (!entity) {
418 // TODO(pvalenzuela): Add logging so that it is easier to determine why 375 LOG(ERROR) << "No server entity was created for client entity with type: "
419 // creation failed. 376 << GetModelType(client_entity)
377 << " and ID: " << client_entity.id_string() << ".";
420 return string(); 378 return string();
421 } 379 }
422 380
423 const std::string id = entity->id(); 381 const std::string id = entity->GetId();
424 SaveEntity(std::move(entity)); 382 SaveEntity(std::move(entity));
425 BuildEntryResponseForSuccessfulCommit(id, entry_response); 383 BuildEntryResponseForSuccessfulCommit(id, entry_response);
426 return id; 384 return id;
427 } 385 }
428 386
429 void FakeServer::BuildEntryResponseForSuccessfulCommit( 387 void LoopbackServer::BuildEntryResponseForSuccessfulCommit(
430 const std::string& entity_id, 388 const std::string& entity_id,
431 sync_pb::CommitResponse_EntryResponse* entry_response) { 389 sync_pb::CommitResponse_EntryResponse* entry_response) {
432 EntityMap::const_iterator iter = entities_.find(entity_id); 390 EntityMap::const_iterator iter = entities_.find(entity_id);
433 CHECK(iter != entities_.end()); 391 CHECK(iter != entities_.end());
434 const FakeServerEntity& entity = *iter->second; 392 const LoopbackServerEntity& entity = *iter->second;
435 entry_response->set_response_type(sync_pb::CommitResponse::SUCCESS); 393 entry_response->set_response_type(sync_pb::CommitResponse::SUCCESS);
436 entry_response->set_id_string(entity.id()); 394 entry_response->set_id_string(entity.GetId());
437 395
438 if (entity.IsDeleted()) { 396 if (entity.IsDeleted()) {
439 entry_response->set_version(entity.GetVersion() + 1); 397 entry_response->set_version(entity.GetVersion() + 1);
440 } else { 398 } else {
441 entry_response->set_version(entity.GetVersion()); 399 entry_response->set_version(entity.GetVersion());
442 entry_response->set_name(entity.GetName()); 400 entry_response->set_name(entity.GetName());
443 } 401 }
444 } 402 }
445 403
446 bool FakeServer::IsChild(const string& id, const string& potential_parent_id) { 404 bool LoopbackServer::IsChild(const string& id,
405 const string& potential_parent_id) {
447 EntityMap::const_iterator iter = entities_.find(id); 406 EntityMap::const_iterator iter = entities_.find(id);
448 if (iter == entities_.end()) { 407 if (iter == entities_.end()) {
449 // We've hit an ID (probably the imaginary root entity) that isn't stored 408 // We've hit an ID (probably the imaginary root entity) that isn't stored
450 // by the server, so it can't be a child. 409 // by the server, so it can't be a child.
451 return false; 410 return false;
452 } 411 }
453 412
454 const FakeServerEntity& entity = *iter->second; 413 const LoopbackServerEntity& entity = *iter->second;
455 if (entity.GetParentId() == potential_parent_id) 414 if (entity.GetParentId() == potential_parent_id)
456 return true; 415 return true;
457 416
458 // Recursively look up the tree. 417 // Recursively look up the tree.
459 return IsChild(entity.GetParentId(), potential_parent_id); 418 return IsChild(entity.GetParentId(), potential_parent_id);
460 } 419 }
461 420
462 void FakeServer::DeleteChildren(const string& id) { 421 void LoopbackServer::DeleteChildren(const string& id) {
463 std::vector<std::unique_ptr<FakeServerEntity>> tombstones; 422 std::vector<sync_pb::SyncEntity> tombstones;
464 // Find all the children of id. 423 // Find all the children of id.
465 for (const auto& entity : entities_) { 424 for (auto& entity : entities_) {
466 if (IsChild(entity.first, id)) { 425 if (IsChild(entity.first, id)) {
467 tombstones.push_back(TombstoneEntity::Create( 426 sync_pb::SyncEntity proto;
468 entity.first, entity.second->client_defined_unique_tag())); 427 entity.second->SerializeAsProto(&proto);
428 tombstones.emplace_back(proto);
469 } 429 }
470 } 430 }
471 431
472 for (auto& tombstone : tombstones) { 432 for (auto& tombstone : tombstones) {
473 SaveEntity(std::move(tombstone)); 433 SaveEntity(PersistentTombstoneEntity::Create(tombstone));
474 } 434 }
475 } 435 }
476 436
477 bool FakeServer::HandleCommitRequest(const sync_pb::CommitMessage& commit, 437 bool LoopbackServer::HandleCommitRequest(
478 const std::string& invalidator_client_id, 438 const sync_pb::CommitMessage& commit,
479 sync_pb::CommitResponse* response) { 439 const std::string& invalidator_client_id,
440 sync_pb::CommitResponse* response) {
480 std::map<string, string> client_to_server_ids; 441 std::map<string, string> client_to_server_ids;
481 string guid = commit.cache_guid(); 442 string guid = commit.cache_guid();
482 ModelTypeSet committed_model_types; 443 ModelTypeSet committed_model_types;
483 444
484 // TODO(pvalenzuela): Add validation of CommitMessage.entries. 445 // TODO(pvalenzuela): Add validation of CommitMessage.entries.
485 ::google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>::const_iterator it; 446 ::google::protobuf::RepeatedPtrField<sync_pb::SyncEntity>::const_iterator it;
486 for (it = commit.entries().begin(); it != commit.entries().end(); ++it) { 447 for (it = commit.entries().begin(); it != commit.entries().end(); ++it) {
487 sync_pb::CommitResponse_EntryResponse* entry_response = 448 sync_pb::CommitResponse_EntryResponse* entry_response =
488 response->add_entryresponse(); 449 response->add_entryresponse();
489 450
490 sync_pb::SyncEntity client_entity = *it; 451 sync_pb::SyncEntity client_entity = *it;
491 string parent_id = client_entity.parent_id_string(); 452 string parent_id = client_entity.parent_id_string();
492 if (client_to_server_ids.find(parent_id) != client_to_server_ids.end()) { 453 if (client_to_server_ids.find(parent_id) != client_to_server_ids.end()) {
493 parent_id = client_to_server_ids[parent_id]; 454 parent_id = client_to_server_ids[parent_id];
494 } 455 }
495 456
496 const string entity_id = 457 const string entity_id =
497 CommitEntity(client_entity, entry_response, guid, parent_id); 458 CommitEntity(client_entity, entry_response, guid, parent_id);
498 if (entity_id.empty()) { 459 if (entity_id.empty()) {
499 return false; 460 return false;
500 } 461 }
501 462
502 // Record the ID if it was renamed. 463 // Record the ID if it was renamed.
503 if (entity_id != client_entity.id_string()) { 464 if (entity_id != client_entity.id_string()) {
504 client_to_server_ids[client_entity.id_string()] = entity_id; 465 client_to_server_ids[client_entity.id_string()] = entity_id;
505 } 466 }
506 467
507 EntityMap::const_iterator iter = entities_.find(entity_id); 468 EntityMap::const_iterator iter = entities_.find(entity_id);
508 CHECK(iter != entities_.end()); 469 CHECK(iter != entities_.end());
509 committed_model_types.Put(iter->second->model_type()); 470 committed_model_types.Put(iter->second->GetModelType());
510 } 471 }
511 472
512 for (auto& observer : observers_)
513 observer.OnCommit(invalidator_client_id, committed_model_types);
514
515 return true; 473 return true;
516 } 474 }
517 475
518 std::unique_ptr<base::DictionaryValue> 476 void LoopbackServer::ClearServerData() {
519 FakeServer::GetEntitiesAsDictionaryValue() {
520 DCHECK(thread_checker_.CalledOnValidThread());
521 std::unique_ptr<base::DictionaryValue> dictionary(
522 new base::DictionaryValue());
523
524 // Initialize an empty ListValue for all ModelTypes.
525 ModelTypeSet all_types = ModelTypeSet::All();
526 for (ModelTypeSet::Iterator it = all_types.First(); it.Good(); it.Inc()) {
527 dictionary->Set(ModelTypeToString(it.Get()), new base::ListValue());
528 }
529
530 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
531 ++it) {
532 const FakeServerEntity& entity = *it->second;
533 if (IsDeletedOrPermanent(entity)) {
534 // Tombstones are ignored as they don't represent current data. Folders
535 // are also ignored as current verification infrastructure does not
536 // consider them.
537 continue;
538 }
539 base::ListValue* list_value;
540 if (!dictionary->GetList(ModelTypeToString(entity.model_type()),
541 &list_value)) {
542 return std::unique_ptr<base::DictionaryValue>();
543 }
544 // TODO(pvalenzuela): Store more data for each entity so additional
545 // verification can be performed. One example of additional verification
546 // is checking the correctness of the bookmark hierarchy.
547 list_value->AppendString(entity.GetName());
548 }
549
550 return dictionary;
551 }
552
553 std::vector<sync_pb::SyncEntity> FakeServer::GetSyncEntitiesByModelType(
554 ModelType model_type) {
555 std::vector<sync_pb::SyncEntity> sync_entities;
556 DCHECK(thread_checker_.CalledOnValidThread());
557 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
558 ++it) {
559 const FakeServerEntity& entity = *it->second;
560 if (!IsDeletedOrPermanent(entity) && entity.model_type() == model_type) {
561 sync_pb::SyncEntity sync_entity;
562 entity.SerializeAsProto(&sync_entity);
563 sync_entities.push_back(sync_entity);
564 }
565 }
566 return sync_entities;
567 }
568
569 void FakeServer::InjectEntity(std::unique_ptr<FakeServerEntity> entity) {
570 DCHECK(thread_checker_.CalledOnValidThread());
571 SaveEntity(std::move(entity));
572 }
573
574 bool FakeServer::ModifyEntitySpecifics(
575 const std::string& id,
576 const sync_pb::EntitySpecifics& updated_specifics) {
577 EntityMap::const_iterator iter = entities_.find(id);
578 if (iter == entities_.end() ||
579 iter->second->model_type() !=
580 GetModelTypeFromSpecifics(updated_specifics)) {
581 return false;
582 }
583
584 FakeServerEntity* entity = iter->second.get();
585 entity->SetSpecifics(updated_specifics);
586 UpdateEntityVersion(entity);
587 return true;
588 }
589
590 bool FakeServer::ModifyBookmarkEntity(
591 const std::string& id,
592 const std::string& parent_id,
593 const sync_pb::EntitySpecifics& updated_specifics) {
594 EntityMap::const_iterator iter = entities_.find(id);
595 if (iter == entities_.end() ||
596 iter->second->model_type() != syncer::BOOKMARKS ||
597 GetModelTypeFromSpecifics(updated_specifics) != syncer::BOOKMARKS) {
598 return false;
599 }
600
601 BookmarkEntity* entity = static_cast<BookmarkEntity*>(iter->second.get());
602
603 entity->SetParentId(parent_id);
604 entity->SetSpecifics(updated_specifics);
605 if (updated_specifics.has_bookmark()) {
606 entity->SetName(updated_specifics.bookmark().title());
607 }
608 UpdateEntityVersion(entity);
609 return true;
610 }
611
612 void FakeServer::ClearServerData() {
613 DCHECK(thread_checker_.CalledOnValidThread()); 477 DCHECK(thread_checker_.CalledOnValidThread());
614 entities_.clear(); 478 entities_.clear();
615 keystore_keys_.clear(); 479 keystore_keys_.clear();
616 ++store_birthday_; 480 ++store_birthday_;
617 Init(); 481 Init();
618 } 482 }
619 483
620 void FakeServer::SetAuthenticated() { 484 std::string LoopbackServer::GetStoreBirthday() const {
621 DCHECK(thread_checker_.CalledOnValidThread());
622 authenticated_ = true;
623 }
624
625 void FakeServer::SetUnauthenticated() {
626 DCHECK(thread_checker_.CalledOnValidThread());
627 authenticated_ = false;
628 }
629
630 bool FakeServer::TriggerError(const sync_pb::SyncEnums::ErrorType& error_type) {
631 DCHECK(thread_checker_.CalledOnValidThread());
632 if (triggered_actionable_error_.get()) {
633 DVLOG(1) << "Only one type of error can be triggered at any given time.";
634 return false;
635 }
636
637 error_type_ = error_type;
638 return true;
639 }
640
641 bool FakeServer::TriggerActionableError(
642 const sync_pb::SyncEnums::ErrorType& error_type,
643 const string& description,
644 const string& url,
645 const sync_pb::SyncEnums::Action& action) {
646 DCHECK(thread_checker_.CalledOnValidThread());
647 if (error_type_ != sync_pb::SyncEnums::SUCCESS) {
648 DVLOG(1) << "Only one type of error can be triggered at any given time.";
649 return false;
650 }
651
652 sync_pb::ClientToServerResponse_Error* error =
653 new sync_pb::ClientToServerResponse_Error();
654 error->set_error_type(error_type);
655 error->set_error_description(description);
656 error->set_url(url);
657 error->set_action(action);
658 triggered_actionable_error_.reset(error);
659 return true;
660 }
661
662 bool FakeServer::EnableAlternatingTriggeredErrors() {
663 DCHECK(thread_checker_.CalledOnValidThread());
664 if (error_type_ == sync_pb::SyncEnums::SUCCESS &&
665 !triggered_actionable_error_.get()) {
666 DVLOG(1) << "No triggered error set. Alternating can't be enabled.";
667 return false;
668 }
669
670 alternate_triggered_errors_ = true;
671 // Reset the counter so that the the first request yields a triggered error.
672 request_counter_ = 0;
673 return true;
674 }
675
676 bool FakeServer::ShouldSendTriggeredError() const {
677 if (!alternate_triggered_errors_)
678 return true;
679
680 // Check that the counter is odd so that we trigger an error on the first
681 // request after alternating is enabled.
682 return request_counter_ % 2 != 0;
683 }
684
685 void FakeServer::AddObserver(Observer* observer) {
686 DCHECK(thread_checker_.CalledOnValidThread());
687 observers_.AddObserver(observer);
688 }
689
690 void FakeServer::RemoveObserver(Observer* observer) {
691 DCHECK(thread_checker_.CalledOnValidThread());
692 observers_.RemoveObserver(observer);
693 }
694
695 void FakeServer::EnableNetwork() {
696 DCHECK(thread_checker_.CalledOnValidThread());
697 network_enabled_ = true;
698 }
699
700 void FakeServer::DisableNetwork() {
701 DCHECK(thread_checker_.CalledOnValidThread());
702 network_enabled_ = false;
703 }
704
705 std::string FakeServer::GetBookmarkBarFolderId() const {
706 DCHECK(thread_checker_.CalledOnValidThread());
707 for (EntityMap::const_iterator it = entities_.begin(); it != entities_.end();
708 ++it) {
709 FakeServerEntity* entity = it->second.get();
710 if (entity->GetName() == kBookmarkBarFolderName && entity->IsFolder() &&
711 entity->model_type() == syncer::BOOKMARKS) {
712 return entity->id();
713 }
714 }
715 NOTREACHED() << "Bookmark Bar entity not found.";
716 return "";
717 }
718
719 base::WeakPtr<FakeServer> FakeServer::AsWeakPtr() {
720 DCHECK(thread_checker_.CalledOnValidThread());
721 return weak_ptr_factory_.GetWeakPtr();
722 }
723
724 std::string FakeServer::GetStoreBirthday() const {
725 DCHECK(thread_checker_.CalledOnValidThread()); 485 DCHECK(thread_checker_.CalledOnValidThread());
726 return base::Int64ToString(store_birthday_); 486 return base::Int64ToString(store_birthday_);
727 } 487 }
728 488
729 } // namespace fake_server 489 void LoopbackServer::SerializeState(sync_pb::LoopbackServerProto* proto) const {
490 DCHECK(thread_checker_.CalledOnValidThread());
491
492 proto->set_version(kCurrentLoopbackServerProtoVersion);
493 proto->set_store_birthday(store_birthday_);
494 proto->set_last_version_assigned(version_);
495 for (const auto& key : keystore_keys_)
496 proto->add_keystore_keys(key);
497 for (const auto& entity : entities_) {
498 auto* new_entity = proto->mutable_entities()->Add();
499 entity.second->SerializeAsLoopbackServerEntity(new_entity);
500 }
501 }
502
503 bool LoopbackServer::DeSerializeState(
504 const sync_pb::LoopbackServerProto& proto) {
505 DCHECK(thread_checker_.CalledOnValidThread());
506 CHECK_EQ(proto.version(), kCurrentLoopbackServerProtoVersion);
507
508 store_birthday_ = proto.store_birthday();
509 version_ = proto.last_version_assigned();
510 for (int i = 0; i < proto.keystore_keys_size(); ++i)
511 keystore_keys_.push_back(proto.keystore_keys(i));
512 for (int i = 0; i < proto.entities_size(); ++i) {
513 entities_[proto.entities(i).entity().id_string()] =
514 LoopbackServerEntity::CreateEntityFromProto(proto.entities(i));
515 }
516
517 return true;
518 }
519
520 // Saves all entities and server state to a protobuf file in |filename|.
521 bool LoopbackServer::SaveStateToFile(const base::FilePath& filename) const {
522 sync_pb::LoopbackServerProto proto;
523 SerializeState(&proto);
524
525 std::string serialized = proto.SerializeAsString();
526 int result = base::WriteFile(filename, serialized.data(), serialized.size());
527 return result == static_cast<int>(serialized.size());
528 }
529
530 // Loads all entities and server state from a protobuf file in |filename|.
531 bool LoopbackServer::LoadStateFromFile(const base::FilePath& filename) {
532 if (base::PathExists(filename)) {
533 std::string serialized;
534 if (base::ReadFileToString(filename, &serialized)) {
535 sync_pb::LoopbackServerProto proto;
536 if (serialized.length() > 0 && proto.ParseFromString(serialized)) {
537 DeSerializeState(proto);
538 return true;
539 } else {
540 LOG(ERROR) << "Loopback sync can not parse the persistent state file.";
541 return false;
542 }
543 } else {
544 // TODO(pastarmovj): Try to understand what is the issue e.g. file already
545 // open, no access rights etc. and decide if better course of action is
546 // available instead of giving up and wiping the global state on the next
547 // write.
548 LOG(ERROR) << "Loopback sync can not read the persistent state file.";
549 return false;
550 }
551 }
552 LOG(WARNING) << "Loopback sync persistent state file does not exist.";
553 return false;
554 }
555
556 } // namespace syncer
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698