Chromium Code Reviews| Index: chrome/browser/sync/engine/syncer_proto_util.cc |
| =================================================================== |
| --- chrome/browser/sync/engine/syncer_proto_util.cc (revision 0) |
| +++ chrome/browser/sync/engine/syncer_proto_util.cc (revision 0) |
| @@ -0,0 +1,276 @@ |
| +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/sync/engine/syncer_proto_util.h" |
| + |
| +#include "chrome/browser/sync/engine/net/server_connection_manager.h" |
| +#include "chrome/browser/sync/engine/syncer.h" |
| +#include "chrome/browser/sync/engine/syncer_util.h" |
| +#include "chrome/browser/sync/protocol/service_constants.h" |
| +#include "chrome/browser/sync/syncable/directory_manager.h" |
| +#include "chrome/browser/sync/syncable/syncable-inl.h" |
| +#include "chrome/browser/sync/syncable/syncable.h" |
| +#include "chrome/browser/sync/util/character_set_converters.h" |
| + |
| +using std::string; |
| +using std::stringstream; |
| +using syncable::BASE_VERSION; |
| +using syncable::CTIME; |
| +using syncable::ID; |
| +using syncable::IS_DEL; |
| +using syncable::IS_DIR; |
| +using syncable::IS_UNSYNCED; |
| +using syncable::MTIME; |
| +using syncable::PARENT_ID; |
| +using syncable::ScopedDirLookup; |
| +using syncable::SyncName; |
| + |
| +namespace browser_sync { |
| + |
| +namespace { |
| + |
| +// Time to backoff syncing after receiving a throttled response. |
| +static const int kSyncDelayAfterThrottled = 2 * 60 * 60; // 2 hours |
| + |
| +// Verifies the store birthday, alerting/resetting as appropriate if there's a |
| +// mismatch. |
| +bool VerifyResponseBirthday(const ScopedDirLookup& dir, |
| + const ClientToServerResponse* response) { |
| + // Process store birthday. |
| + if (!response->has_store_birthday()) |
| + return true; |
| + string birthday = dir->store_birthday(); |
| + if (response->store_birthday() == birthday) |
| + return true; |
| + LOG(INFO) << "New store birthday: " << response->store_birthday(); |
| + if (!birthday.empty()) { |
| + LOG(ERROR) << "Birthday changed, showing syncer stuck"; |
| + return false; |
| + } |
| + dir->set_store_birthday(response->store_birthday()); |
| + return true; |
| +} |
| + |
| +void LogResponseProfilingData(const ClientToServerResponse& response) { |
| + if (response.has_profiling_data()) { |
| + stringstream response_trace; |
| + response_trace << "Server response trace:"; |
| + |
| + if (response.profiling_data().has_user_lookup_time()) { |
| + response_trace << " " << "user lookup: " << |
| + response.profiling_data().user_lookup_time() << "ms"; |
| + } |
| + |
| + if (response.profiling_data().has_meta_data_write_time()) { |
| + response_trace << " " << "meta write: " << |
| + response.profiling_data().meta_data_write_time() << "ms"; |
| + } |
| + |
| + if (response.profiling_data().has_meta_data_read_time()) { |
| + response_trace << " " << "meta read: " << |
| + response.profiling_data().meta_data_read_time() << "ms"; |
| + } |
| + |
| + if (response.profiling_data().has_file_data_write_time()) { |
| + response_trace << " " << "file write: " << |
| + response.profiling_data().file_data_write_time() << "ms"; |
| + } |
| + |
| + if (response.profiling_data().has_file_data_read_time()) { |
| + response_trace << " " << "file read: " << |
| + response.profiling_data().file_data_read_time() << "ms"; |
| + } |
| + |
| + if (response.profiling_data().has_total_request_time()) { |
| + response_trace << " " << "total time: " << |
| + response.profiling_data().total_request_time() << "ms"; |
| + } |
| + LOG(INFO) << response_trace.str(); |
| + } |
| +} |
| + |
| +} // namespace |
| + |
| +// static |
| +bool SyncerProtoUtil::PostClientToServerMessage( |
| + ClientToServerMessage* msg, |
| + ClientToServerResponse* response, |
| + SyncerSession *session) { |
|
idana
2009/09/10 05:44:37
Same old "SyncerSession *session"...
|
| + |
| + bool rv = false; |
| + string tx, rx; |
| + CHECK(response); |
| + |
| + ScopedDirLookup dir(session->dirman(), session->account_name()); |
| + if (!dir.good()) |
| + return false; |
| + string birthday = dir->store_birthday(); |
| + if (!birthday.empty()) { |
| + msg->set_store_birthday(birthday); |
| + } else { |
| + LOG(INFO) << "no birthday set"; |
| + } |
| + |
| + msg->SerializeToString(&tx); |
| + HttpResponse http_response; |
| + ServerConnectionManager::PostBufferParams params = { |
| + tx, &rx, &http_response |
| + }; |
| + |
| + if (!session->connection_manager()->PostBufferWithCachedAuth(¶ms)) { |
| + LOG(WARNING) << "Error posting from syncer:" << http_response; |
| + } else { |
| + rv = response->ParseFromString(rx); |
| + } |
| + SyncerStatus status(session); |
| + if (rv) { |
| + if (!VerifyResponseBirthday(dir, response)) { |
| + // TODO(ncarter): Add a unit test for the case where the syncer |
| + // becomes stuck due to a bad birthday. |
| + status.set_syncer_stuck(true); |
| + return false; |
| + } |
| + |
| + // We use an exponential moving average to determine the rate of errors. |
| + // It's more reactive to recent situations and uses no extra storage. |
| + status.ForgetOldError(); |
| + // If we're decaying send out an update. |
| + status.CheckErrorRateTooHigh(); |
| + |
| + switch (response->error_code()) { |
| + case ClientToServerResponse::SUCCESS: |
| + if (!response->has_store_birthday() && birthday.empty()) { |
| + LOG(ERROR) << |
| + "Server didn't provide birthday in proto buffer response."; |
| + rv = false; |
| + } |
| + LogResponseProfilingData(*response); |
| + break; |
| + case ClientToServerResponse::USER_NOT_ACTIVATED: |
| + case ClientToServerResponse::AUTH_INVALID: |
| + case ClientToServerResponse::ACCESS_DENIED: |
| + LOG(INFO) << "Authentication expired, re-requesting"; |
| + LOG(INFO) << "Not implemented in syncer yet!!!"; |
| + status.AuthFailed(); |
| + rv = false; |
| + break; |
| + case ClientToServerResponse::NOT_MY_BIRTHDAY: |
| + LOG(WARNING) << "Not my birthday return."; |
| + rv = false; |
| + break; |
| + case ClientToServerResponse::THROTTLED: |
| + LOG(WARNING) << "Client silenced by server."; |
| + session->set_silenced_until(time(0) + kSyncDelayAfterThrottled); |
| + rv = false; |
| + break; |
| + } |
| + |
| + } else if (session->connection_manager()->IsServerReachable()) { |
| + status.TallyNewError(); |
| + } |
| + return rv; |
| +} |
| + |
| +// static |
| +bool SyncerProtoUtil::Compare(const syncable::Entry& local_entry, |
| + const SyncEntity& server_entry) { |
| + SyncName name = NameFromSyncEntity(server_entry); |
| + |
| + CHECK(local_entry.Get(ID) == server_entry.id()) << |
| + " SyncerProtoUtil::Compare precondition not met."; |
| + CHECK(server_entry.version() == local_entry.Get(BASE_VERSION)) << |
| + " SyncerProtoUtil::Compare precondition not met."; |
| + CHECK(!local_entry.Get(IS_UNSYNCED)) << |
| + " SyncerProtoUtil::Compare precondition not met."; |
| + |
| + if (local_entry.Get(IS_DEL) && server_entry.deleted()) |
| + return true; |
| + if (!ClientAndServerTimeMatch(local_entry.Get(CTIME), server_entry.ctime())) { |
| + LOG(WARNING) << "ctime mismatch"; |
| + return false; |
| + } |
| + |
| + // These checks are somewhat prolix, but they're easier to debug than |
| + // a big boolean statement. |
| + SyncName client_name = local_entry.GetName(); |
| + if (client_name != name) { |
| + LOG(WARNING) << "Client name mismatch"; |
| + return false; |
| + } |
| + if (local_entry.Get(PARENT_ID) != server_entry.parent_id()) { |
| + LOG(WARNING) << "Parent ID mismatch"; |
| + return false; |
| + } |
| + if (local_entry.Get(IS_DIR) != server_entry.IsFolder()) { |
| + LOG(WARNING) << "Dir field mismatch"; |
| + return false; |
| + } |
| + if (local_entry.Get(IS_DEL) != server_entry.deleted()) { |
| + LOG(WARNING) << "Deletion mismatch"; |
| + return false; |
| + } |
| + if (!local_entry.Get(IS_DIR) && |
| + !ClientAndServerTimeMatch(local_entry.Get(MTIME), |
| + server_entry.mtime())) { |
| + LOG(WARNING) << "mtime mismatch"; |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +// static |
| +void SyncerProtoUtil::CopyProtoBytesIntoBlob(const std::string& proto_bytes, |
| + syncable::Blob* blob) { |
| + syncable::Blob proto_blob(proto_bytes.begin(), proto_bytes.end()); |
| + blob->swap(proto_blob); |
| +} |
| + |
| +// static |
| +bool SyncerProtoUtil::ProtoBytesEqualsBlob(const std::string& proto_bytes, |
| + const syncable::Blob& blob) { |
| + if (proto_bytes.size() != blob.size()) |
| + return false; |
| + return std::equal(proto_bytes.begin(), proto_bytes.end(), blob.begin()); |
| +} |
| + |
| +// static |
| +void SyncerProtoUtil::CopyBlobIntoProtoBytes(const syncable::Blob& blob, |
| + std::string* proto_bytes) { |
| + std::string blob_string(blob.begin(), blob.end()); |
| + proto_bytes->swap(blob_string); |
| +} |
| + |
| +// static |
| +syncable::SyncName SyncerProtoUtil::NameFromSyncEntity( |
| + const SyncEntity &entry) { |
| + SyncName result(PSTR("")); |
| + |
| + AppendUTF8ToPathString(entry.name(), &result.value()); |
| + if (entry.has_non_unique_name()) { |
| + AppendUTF8ToPathString(entry.non_unique_name(), |
| + &result.non_unique_value()); |
| + } else { |
| + result.non_unique_value() = result.value(); |
| + } |
| + return result; |
| +} |
| + |
| +// static |
| +syncable::SyncName SyncerProtoUtil::NameFromCommitEntryResponse( |
| + const CommitResponse_EntryResponse& entry) { |
| + SyncName result(PSTR("")); |
| + |
| + AppendUTF8ToPathString(entry.name(), &result.value()); |
| + if (entry.has_non_unique_name()) { |
| + AppendUTF8ToPathString(entry.non_unique_name(), |
| + &result.non_unique_value()); |
| + } else { |
| + result.non_unique_value() = result.value(); |
| + } |
| + return result; |
| +} |
| + |
|
idana
2009/09/10 05:44:37
Remove extra blank line.
|
| + |
| +} // namespace browser_sync |
| Property changes on: chrome\browser\sync\engine\syncer_proto_util.cc |
| ___________________________________________________________________ |
| Added: svn:eol-style |
| + LF |