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

Side by Side Diff: chrome/browser/sync/engine/syncer_proto_util.cc

Issue 9699057: [Sync] Move 'sync' target to sync/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address Tim's comments Created 8 years, 9 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/sync/engine/syncer_proto_util.h"
6
7 #include "base/format_macros.h"
8 #include "base/stringprintf.h"
9 #include "chrome/browser/sync/engine/net/server_connection_manager.h"
10 #include "chrome/browser/sync/engine/syncer.h"
11 #include "chrome/browser/sync/engine/syncer_types.h"
12 #include "chrome/browser/sync/protocol/service_constants.h"
13 #include "chrome/browser/sync/protocol/sync_protocol_error.h"
14 #include "chrome/browser/sync/sessions/sync_session.h"
15 #include "chrome/browser/sync/syncable/model_type.h"
16 #include "chrome/browser/sync/syncable/syncable-inl.h"
17 #include "chrome/browser/sync/syncable/syncable.h"
18 #include "chrome/browser/sync/util/time.h"
19 #include "sync/protocol/sync.pb.h"
20 #include "sync/protocol/sync_enums.pb.h"
21
22 using browser_sync::SyncProtocolErrorType;
23 using std::string;
24 using std::stringstream;
25 using syncable::BASE_VERSION;
26 using syncable::CTIME;
27 using syncable::ID;
28 using syncable::IS_DEL;
29 using syncable::IS_DIR;
30 using syncable::IS_UNSYNCED;
31 using syncable::MTIME;
32 using syncable::PARENT_ID;
33
34 namespace browser_sync {
35 using sessions::SyncSession;
36
37 namespace {
38
39 // Time to backoff syncing after receiving a throttled response.
40 const int kSyncDelayAfterThrottled = 2 * 60 * 60; // 2 hours
41
42 void LogResponseProfilingData(const ClientToServerResponse& response) {
43 if (response.has_profiling_data()) {
44 stringstream response_trace;
45 response_trace << "Server response trace:";
46
47 if (response.profiling_data().has_user_lookup_time()) {
48 response_trace << " user lookup: "
49 << response.profiling_data().user_lookup_time() << "ms";
50 }
51
52 if (response.profiling_data().has_meta_data_write_time()) {
53 response_trace << " meta write: "
54 << response.profiling_data().meta_data_write_time()
55 << "ms";
56 }
57
58 if (response.profiling_data().has_meta_data_read_time()) {
59 response_trace << " meta read: "
60 << response.profiling_data().meta_data_read_time() << "ms";
61 }
62
63 if (response.profiling_data().has_file_data_write_time()) {
64 response_trace << " file write: "
65 << response.profiling_data().file_data_write_time()
66 << "ms";
67 }
68
69 if (response.profiling_data().has_file_data_read_time()) {
70 response_trace << " file read: "
71 << response.profiling_data().file_data_read_time() << "ms";
72 }
73
74 if (response.profiling_data().has_total_request_time()) {
75 response_trace << " total time: "
76 << response.profiling_data().total_request_time() << "ms";
77 }
78 DVLOG(1) << response_trace.str();
79 }
80 }
81
82 SyncerError ServerConnectionErrorAsSyncerError(
83 const HttpResponse::ServerConnectionCode server_status) {
84 switch (server_status) {
85 case HttpResponse::CONNECTION_UNAVAILABLE:
86 return NETWORK_CONNECTION_UNAVAILABLE;
87 case HttpResponse::IO_ERROR:
88 return NETWORK_IO_ERROR;
89 case HttpResponse::SYNC_SERVER_ERROR:
90 // FIXME what does this mean?
91 return SYNC_SERVER_ERROR;
92 case HttpResponse::SYNC_AUTH_ERROR:
93 return SYNC_AUTH_ERROR;
94 case HttpResponse::RETRY:
95 return SERVER_RETURN_TRANSIENT_ERROR;
96 case HttpResponse::SERVER_CONNECTION_OK:
97 case HttpResponse::NONE:
98 default:
99 NOTREACHED();
100 return UNSET;
101 }
102 }
103
104 } // namespace
105
106 // static
107 void SyncerProtoUtil::HandleMigrationDoneResponse(
108 const sync_pb::ClientToServerResponse* response,
109 sessions::SyncSession* session) {
110 LOG_IF(ERROR, 0 >= response->migrated_data_type_id_size())
111 << "MIGRATION_DONE but no types specified.";
112 syncable::ModelTypeSet to_migrate;
113 for (int i = 0; i < response->migrated_data_type_id_size(); i++) {
114 to_migrate.Put(syncable::GetModelTypeFromSpecificsFieldNumber(
115 response->migrated_data_type_id(i)));
116 }
117 // TODO(akalin): This should be a set union.
118 session->mutable_status_controller()->
119 set_types_needing_local_migration(to_migrate);
120 }
121
122 // static
123 bool SyncerProtoUtil::VerifyResponseBirthday(syncable::Directory* dir,
124 const ClientToServerResponse* response) {
125
126 std::string local_birthday = dir->store_birthday();
127
128 if (local_birthday.empty()) {
129 if (!response->has_store_birthday()) {
130 LOG(WARNING) << "Expected a birthday on first sync.";
131 return false;
132 }
133
134 DVLOG(1) << "New store birthday: " << response->store_birthday();
135 dir->set_store_birthday(response->store_birthday());
136 return true;
137 }
138
139 // Error situation, but we're not stuck.
140 if (!response->has_store_birthday()) {
141 LOG(WARNING) << "No birthday in server response?";
142 return true;
143 }
144
145 if (response->store_birthday() != local_birthday) {
146 LOG(WARNING) << "Birthday changed, showing syncer stuck";
147 return false;
148 }
149
150 return true;
151 }
152
153 // static
154 void SyncerProtoUtil::AddRequestBirthday(syncable::Directory* dir,
155 ClientToServerMessage* msg) {
156 if (!dir->store_birthday().empty())
157 msg->set_store_birthday(dir->store_birthday());
158 }
159
160 // static
161 bool SyncerProtoUtil::PostAndProcessHeaders(ServerConnectionManager* scm,
162 sessions::SyncSession* session,
163 const ClientToServerMessage& msg,
164 ClientToServerResponse* response) {
165 ServerConnectionManager::PostBufferParams params;
166 msg.SerializeToString(&params.buffer_in);
167
168 ScopedServerStatusWatcher server_status_watcher(scm, &params.response);
169 // Fills in params.buffer_out and params.response.
170 if (!scm->PostBufferWithCachedAuth(&params, &server_status_watcher)) {
171 LOG(WARNING) << "Error posting from syncer:" << params.response;
172 return false;
173 }
174
175 std::string new_token = params.response.update_client_auth_header;
176 if (!new_token.empty()) {
177 SyncEngineEvent event(SyncEngineEvent::UPDATED_TOKEN);
178 event.updated_token = new_token;
179 session->context()->NotifyListeners(event);
180 }
181
182 if (response->ParseFromString(params.buffer_out)) {
183 // TODO(tim): This is an egregious layering violation (bug 35060).
184 switch (response->error_code()) {
185 case sync_pb::SyncEnums::ACCESS_DENIED:
186 case sync_pb::SyncEnums::AUTH_INVALID:
187 case sync_pb::SyncEnums::USER_NOT_ACTIVATED:
188 // Fires on ScopedServerStatusWatcher
189 params.response.server_status = HttpResponse::SYNC_AUTH_ERROR;
190 return false;
191 default:
192 return true;
193 }
194 }
195
196 return false;
197 }
198
199 base::TimeDelta SyncerProtoUtil::GetThrottleDelay(
200 const sync_pb::ClientToServerResponse& response) {
201 base::TimeDelta throttle_delay =
202 base::TimeDelta::FromSeconds(kSyncDelayAfterThrottled);
203 if (response.has_client_command()) {
204 const sync_pb::ClientCommand& command = response.client_command();
205 if (command.has_throttle_delay_seconds()) {
206 throttle_delay =
207 base::TimeDelta::FromSeconds(command.throttle_delay_seconds());
208 }
209 }
210 return throttle_delay;
211 }
212
213 void SyncerProtoUtil::HandleThrottleError(
214 const SyncProtocolError& error,
215 const base::TimeTicks& throttled_until,
216 sessions::SyncSessionContext* context,
217 sessions::SyncSession::Delegate* delegate) {
218 DCHECK_EQ(error.error_type, browser_sync::THROTTLED);
219 if (error.error_data_types.Empty()) {
220 // No datatypes indicates the client should be completely throttled.
221 delegate->OnSilencedUntil(throttled_until);
222 } else {
223 context->SetUnthrottleTime(error.error_data_types, throttled_until);
224 }
225 }
226
227 namespace {
228
229 // Helper function for an assertion in PostClientToServerMessage.
230 bool IsVeryFirstGetUpdates(const ClientToServerMessage& message) {
231 if (!message.has_get_updates())
232 return false;
233 DCHECK_LT(0, message.get_updates().from_progress_marker_size());
234 for (int i = 0; i < message.get_updates().from_progress_marker_size(); ++i) {
235 if (!message.get_updates().from_progress_marker(i).token().empty())
236 return false;
237 }
238 return true;
239 }
240
241 SyncProtocolErrorType ConvertSyncProtocolErrorTypePBToLocalType(
242 const sync_pb::SyncEnums::ErrorType& error_type) {
243 switch (error_type) {
244 case sync_pb::SyncEnums::SUCCESS:
245 return browser_sync::SYNC_SUCCESS;
246 case sync_pb::SyncEnums::NOT_MY_BIRTHDAY:
247 return browser_sync::NOT_MY_BIRTHDAY;
248 case sync_pb::SyncEnums::THROTTLED:
249 return browser_sync::THROTTLED;
250 case sync_pb::SyncEnums::CLEAR_PENDING:
251 return browser_sync::CLEAR_PENDING;
252 case sync_pb::SyncEnums::TRANSIENT_ERROR:
253 return browser_sync::TRANSIENT_ERROR;
254 case sync_pb::SyncEnums::MIGRATION_DONE:
255 return browser_sync::MIGRATION_DONE;
256 case sync_pb::SyncEnums::UNKNOWN:
257 return browser_sync::UNKNOWN_ERROR;
258 case sync_pb::SyncEnums::USER_NOT_ACTIVATED:
259 case sync_pb::SyncEnums::AUTH_INVALID:
260 case sync_pb::SyncEnums::ACCESS_DENIED:
261 return browser_sync::INVALID_CREDENTIAL;
262 default:
263 NOTREACHED();
264 return browser_sync::UNKNOWN_ERROR;
265 }
266 }
267
268 browser_sync::ClientAction ConvertClientActionPBToLocalClientAction(
269 const sync_pb::ClientToServerResponse::Error::Action& action) {
270 switch (action) {
271 case ClientToServerResponse::Error::UPGRADE_CLIENT:
272 return browser_sync::UPGRADE_CLIENT;
273 case ClientToServerResponse::Error::CLEAR_USER_DATA_AND_RESYNC:
274 return browser_sync::CLEAR_USER_DATA_AND_RESYNC;
275 case ClientToServerResponse::Error::ENABLE_SYNC_ON_ACCOUNT:
276 return browser_sync::ENABLE_SYNC_ON_ACCOUNT;
277 case ClientToServerResponse::Error::STOP_AND_RESTART_SYNC:
278 return browser_sync::STOP_AND_RESTART_SYNC;
279 case ClientToServerResponse::Error::DISABLE_SYNC_ON_CLIENT:
280 return browser_sync::DISABLE_SYNC_ON_CLIENT;
281 case ClientToServerResponse::Error::UNKNOWN_ACTION:
282 return browser_sync::UNKNOWN_ACTION;
283 default:
284 NOTREACHED();
285 return browser_sync::UNKNOWN_ACTION;
286 }
287 }
288
289 browser_sync::SyncProtocolError ConvertErrorPBToLocalType(
290 const sync_pb::ClientToServerResponse::Error& error) {
291 browser_sync::SyncProtocolError sync_protocol_error;
292 sync_protocol_error.error_type = ConvertSyncProtocolErrorTypePBToLocalType(
293 error.error_type());
294 sync_protocol_error.error_description = error.error_description();
295 sync_protocol_error.url = error.url();
296 sync_protocol_error.action = ConvertClientActionPBToLocalClientAction(
297 error.action());
298
299 if (error.error_data_type_ids_size() > 0) {
300 // THROTTLED is currently the only error code that uses |error_data_types|.
301 DCHECK_EQ(error.error_type(), sync_pb::SyncEnums::THROTTLED);
302 for (int i = 0; i < error.error_data_type_ids_size(); ++i) {
303 sync_protocol_error.error_data_types.Put(
304 syncable::GetModelTypeFromSpecificsFieldNumber(
305 error.error_data_type_ids(i)));
306 }
307 }
308
309 return sync_protocol_error;
310 }
311
312 // TODO(lipalani) : Rename these function names as per the CR for issue 7740067.
313 browser_sync::SyncProtocolError ConvertLegacyErrorCodeToNewError(
314 const sync_pb::SyncEnums::ErrorType& error_type) {
315 browser_sync::SyncProtocolError error;
316 error.error_type = ConvertSyncProtocolErrorTypePBToLocalType(error_type);
317 if (error_type == sync_pb::SyncEnums::CLEAR_PENDING ||
318 error_type == sync_pb::SyncEnums::NOT_MY_BIRTHDAY) {
319 error.action = browser_sync::DISABLE_SYNC_ON_CLIENT;
320 } // There is no other action we can compute for legacy server.
321 return error;
322 }
323
324 } // namespace
325
326 // static
327 SyncerError SyncerProtoUtil::PostClientToServerMessage(
328 const ClientToServerMessage& msg,
329 ClientToServerResponse* response,
330 SyncSession* session) {
331
332 CHECK(response);
333 DCHECK(!msg.get_updates().has_from_timestamp()); // Deprecated.
334 DCHECK(!msg.get_updates().has_requested_types()); // Deprecated.
335 DCHECK(msg.has_store_birthday() || IsVeryFirstGetUpdates(msg))
336 << "Must call AddRequestBirthday to set birthday.";
337
338 syncable::Directory* dir = session->context()->directory();
339
340 if (!PostAndProcessHeaders(session->context()->connection_manager(), session,
341 msg, response)) {
342 // There was an error establishing communication with the server.
343 // We can not proceed beyond this point.
344 const browser_sync::HttpResponse::ServerConnectionCode server_status =
345 session->context()->connection_manager()->server_status();
346
347 DCHECK_NE(server_status, browser_sync::HttpResponse::NONE);
348 DCHECK_NE(server_status, browser_sync::HttpResponse::SERVER_CONNECTION_OK);
349
350 return ServerConnectionErrorAsSyncerError(server_status);
351 }
352
353 browser_sync::SyncProtocolError sync_protocol_error;
354
355 // Birthday mismatch overrides any error that is sent by the server.
356 if (!VerifyResponseBirthday(dir, response)) {
357 sync_protocol_error.error_type = browser_sync::NOT_MY_BIRTHDAY;
358 sync_protocol_error.action =
359 browser_sync::DISABLE_SYNC_ON_CLIENT;
360 } else if (response->has_error()) {
361 // This is a new server. Just get the error from the protocol.
362 sync_protocol_error = ConvertErrorPBToLocalType(response->error());
363 } else {
364 // Legacy server implementation. Compute the error based on |error_code|.
365 sync_protocol_error = ConvertLegacyErrorCodeToNewError(
366 response->error_code());
367 }
368
369 // Now set the error into the status so the layers above us could read it.
370 sessions::StatusController* status = session->mutable_status_controller();
371 status->set_sync_protocol_error(sync_protocol_error);
372
373 // Inform the delegate of the error we got.
374 session->delegate()->OnSyncProtocolError(session->TakeSnapshot());
375
376 // Now do any special handling for the error type and decide on the return
377 // value.
378 switch (sync_protocol_error.error_type) {
379 case browser_sync::UNKNOWN_ERROR:
380 LOG(WARNING) << "Sync protocol out-of-date. The server is using a more "
381 << "recent version.";
382 return SERVER_RETURN_UNKNOWN_ERROR;
383 case browser_sync::SYNC_SUCCESS:
384 LogResponseProfilingData(*response);
385 return SYNCER_OK;
386 case browser_sync::THROTTLED:
387 LOG(WARNING) << "Client silenced by server.";
388 HandleThrottleError(sync_protocol_error,
389 base::TimeTicks::Now() + GetThrottleDelay(*response),
390 session->context(),
391 session->delegate());
392 return SERVER_RETURN_THROTTLED;
393 case browser_sync::TRANSIENT_ERROR:
394 return SERVER_RETURN_TRANSIENT_ERROR;
395 case browser_sync::MIGRATION_DONE:
396 HandleMigrationDoneResponse(response, session);
397 return SERVER_RETURN_MIGRATION_DONE;
398 case browser_sync::CLEAR_PENDING:
399 return SERVER_RETURN_CLEAR_PENDING;
400 case browser_sync::NOT_MY_BIRTHDAY:
401 return SERVER_RETURN_NOT_MY_BIRTHDAY;
402 default:
403 NOTREACHED();
404 return UNSET;
405 }
406 }
407
408 // static
409 bool SyncerProtoUtil::Compare(const syncable::Entry& local_entry,
410 const SyncEntity& server_entry) {
411 const std::string name = NameFromSyncEntity(server_entry);
412
413 CHECK(local_entry.Get(ID) == server_entry.id()) <<
414 " SyncerProtoUtil::Compare precondition not met.";
415 CHECK(server_entry.version() == local_entry.Get(BASE_VERSION)) <<
416 " SyncerProtoUtil::Compare precondition not met.";
417 CHECK(!local_entry.Get(IS_UNSYNCED)) <<
418 " SyncerProtoUtil::Compare precondition not met.";
419
420 if (local_entry.Get(IS_DEL) && server_entry.deleted())
421 return true;
422 if (local_entry.Get(CTIME) != ProtoTimeToTime(server_entry.ctime())) {
423 LOG(WARNING) << "ctime mismatch";
424 return false;
425 }
426
427 // These checks are somewhat prolix, but they're easier to debug than a big
428 // boolean statement.
429 string client_name = local_entry.Get(syncable::NON_UNIQUE_NAME);
430 if (client_name != name) {
431 LOG(WARNING) << "Client name mismatch";
432 return false;
433 }
434 if (local_entry.Get(PARENT_ID) != server_entry.parent_id()) {
435 LOG(WARNING) << "Parent ID mismatch";
436 return false;
437 }
438 if (local_entry.Get(IS_DIR) != server_entry.IsFolder()) {
439 LOG(WARNING) << "Dir field mismatch";
440 return false;
441 }
442 if (local_entry.Get(IS_DEL) != server_entry.deleted()) {
443 LOG(WARNING) << "Deletion mismatch";
444 return false;
445 }
446 if (!local_entry.Get(IS_DIR) &&
447 (local_entry.Get(MTIME) != ProtoTimeToTime(server_entry.mtime()))) {
448 LOG(WARNING) << "mtime mismatch";
449 return false;
450 }
451
452 return true;
453 }
454
455 // static
456 void SyncerProtoUtil::CopyProtoBytesIntoBlob(const std::string& proto_bytes,
457 syncable::Blob* blob) {
458 syncable::Blob proto_blob(proto_bytes.begin(), proto_bytes.end());
459 blob->swap(proto_blob);
460 }
461
462 // static
463 bool SyncerProtoUtil::ProtoBytesEqualsBlob(const std::string& proto_bytes,
464 const syncable::Blob& blob) {
465 if (proto_bytes.size() != blob.size())
466 return false;
467 return std::equal(proto_bytes.begin(), proto_bytes.end(), blob.begin());
468 }
469
470 // static
471 void SyncerProtoUtil::CopyBlobIntoProtoBytes(const syncable::Blob& blob,
472 std::string* proto_bytes) {
473 std::string blob_string(blob.begin(), blob.end());
474 proto_bytes->swap(blob_string);
475 }
476
477 // static
478 const std::string& SyncerProtoUtil::NameFromSyncEntity(
479 const sync_pb::SyncEntity& entry) {
480 if (entry.has_non_unique_name())
481 return entry.non_unique_name();
482 return entry.name();
483 }
484
485 // static
486 const std::string& SyncerProtoUtil::NameFromCommitEntryResponse(
487 const CommitResponse_EntryResponse& entry) {
488 if (entry.has_non_unique_name())
489 return entry.non_unique_name();
490 return entry.name();
491 }
492
493 std::string SyncerProtoUtil::SyncEntityDebugString(
494 const sync_pb::SyncEntity& entry) {
495 const std::string& mtime_str =
496 GetTimeDebugString(ProtoTimeToTime(entry.mtime()));
497 const std::string& ctime_str =
498 GetTimeDebugString(ProtoTimeToTime(entry.ctime()));
499 return base::StringPrintf(
500 "id: %s, parent_id: %s, "
501 "version: %"PRId64"d, "
502 "mtime: %" PRId64"d (%s), "
503 "ctime: %" PRId64"d (%s), "
504 "name: %s, sync_timestamp: %" PRId64"d, "
505 "%s ",
506 entry.id_string().c_str(),
507 entry.parent_id_string().c_str(),
508 entry.version(),
509 entry.mtime(), mtime_str.c_str(),
510 entry.ctime(), ctime_str.c_str(),
511 entry.name().c_str(), entry.sync_timestamp(),
512 entry.deleted() ? "deleted, ":"");
513 }
514
515 namespace {
516 std::string GetUpdatesResponseString(
517 const sync_pb::GetUpdatesResponse& response) {
518 std::string output;
519 output.append("GetUpdatesResponse:\n");
520 for (int i = 0; i < response.entries_size(); i++) {
521 output.append(SyncerProtoUtil::SyncEntityDebugString(response.entries(i)));
522 output.append("\n");
523 }
524 return output;
525 }
526 } // namespace
527
528 std::string SyncerProtoUtil::ClientToServerResponseDebugString(
529 const sync_pb::ClientToServerResponse& response) {
530 // Add more handlers as needed.
531 std::string output;
532 if (response.has_get_updates())
533 output.append(GetUpdatesResponseString(response.get_updates()));
534 return output;
535 }
536
537 } // namespace browser_sync
OLDNEW
« no previous file with comments | « chrome/browser/sync/engine/syncer_proto_util.h ('k') | chrome/browser/sync/engine/syncer_proto_util_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698