123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614 |
- // Copyright (c) 2012 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 "components/sync/engine/syncer_proto_util.h"
- #include <map>
- #include "base/format_macros.h"
- #include "base/logging.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/strings/stringprintf.h"
- #include "base/time/time.h"
- #include "components/sync/base/model_type.h"
- #include "components/sync/base/time.h"
- #include "components/sync/engine/cycle/sync_cycle_context.h"
- #include "components/sync/engine/net/server_connection_manager.h"
- #include "components/sync/engine/syncer.h"
- #include "components/sync/engine/traffic_logger.h"
- #include "components/sync/protocol/data_type_progress_marker.pb.h"
- #include "components/sync/protocol/sync_enums.pb.h"
- #include "components/sync/protocol/sync_protocol_error.h"
- #include "google_apis/google_api_keys.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- using std::string;
- using std::stringstream;
- using sync_pb::ClientToServerMessage;
- using sync_pb::ClientToServerResponse;
- namespace syncer {
- namespace {
- // Time to backoff syncing after receiving a throttled response.
- constexpr base::TimeDelta kSyncDelayAfterThrottled = base::Hours(2);
- SyncerError ServerConnectionErrorAsSyncerError(
- const HttpResponse::ServerConnectionCode server_status,
- int net_error_code,
- int http_status_code) {
- switch (server_status) {
- case HttpResponse::CONNECTION_UNAVAILABLE:
- return SyncerError::NetworkConnectionUnavailable(net_error_code);
- case HttpResponse::IO_ERROR:
- return SyncerError(SyncerError::NETWORK_IO_ERROR);
- case HttpResponse::SYNC_SERVER_ERROR:
- // This means the server returned a non-401 HTTP error.
- return SyncerError::HttpError(http_status_code);
- case HttpResponse::SYNC_AUTH_ERROR:
- // This means the server returned an HTTP 401 (unauthorized) error.
- return SyncerError::HttpError(http_status_code);
- case HttpResponse::SERVER_CONNECTION_OK:
- case HttpResponse::NONE:
- default:
- NOTREACHED();
- return SyncerError();
- }
- }
- SyncProtocolErrorType PBErrorTypeToSyncProtocolErrorType(
- const sync_pb::SyncEnums::ErrorType& error_type) {
- switch (error_type) {
- case sync_pb::SyncEnums::SUCCESS:
- return SYNC_SUCCESS;
- case sync_pb::SyncEnums::NOT_MY_BIRTHDAY:
- return NOT_MY_BIRTHDAY;
- case sync_pb::SyncEnums::THROTTLED:
- return THROTTLED;
- case sync_pb::SyncEnums::CLEAR_PENDING:
- return CLEAR_PENDING;
- case sync_pb::SyncEnums::TRANSIENT_ERROR:
- return TRANSIENT_ERROR;
- case sync_pb::SyncEnums::MIGRATION_DONE:
- return MIGRATION_DONE;
- case sync_pb::SyncEnums::DISABLED_BY_ADMIN:
- return DISABLED_BY_ADMIN;
- case sync_pb::SyncEnums::PARTIAL_FAILURE:
- return PARTIAL_FAILURE;
- case sync_pb::SyncEnums::CLIENT_DATA_OBSOLETE:
- return CLIENT_DATA_OBSOLETE;
- case sync_pb::SyncEnums::UNKNOWN:
- return UNKNOWN_ERROR;
- case sync_pb::SyncEnums::ENCRYPTION_OBSOLETE:
- return ENCRYPTION_OBSOLETE;
- }
- NOTREACHED();
- return UNKNOWN_ERROR;
- }
- ClientAction PBActionToClientAction(const sync_pb::SyncEnums::Action& action) {
- switch (action) {
- case sync_pb::SyncEnums::UPGRADE_CLIENT:
- return UPGRADE_CLIENT;
- case sync_pb::SyncEnums::UNKNOWN_ACTION:
- return UNKNOWN_ACTION;
- }
- NOTREACHED();
- return UNKNOWN_ACTION;
- }
- // Returns true iff |message| is an initial GetUpdates request.
- bool IsVeryFirstGetUpdates(const ClientToServerMessage& message) {
- if (!message.has_get_updates())
- return false;
- DCHECK_LT(0, message.get_updates().from_progress_marker_size());
- for (int i = 0; i < message.get_updates().from_progress_marker_size(); ++i) {
- if (!message.get_updates().from_progress_marker(i).token().empty())
- return false;
- }
- return true;
- }
- // Returns true iff |message| should contain a store birthday.
- bool IsBirthdayRequired(const ClientToServerMessage& message) {
- if (message.has_clear_server_data())
- return false;
- if (message.has_commit())
- return true;
- if (message.has_get_updates())
- return !IsVeryFirstGetUpdates(message);
- NOTIMPLEMENTED();
- return true;
- }
- SyncProtocolError ErrorCodeToSyncProtocolError(
- const sync_pb::SyncEnums::ErrorType& error_type) {
- SyncProtocolError error;
- error.error_type = PBErrorTypeToSyncProtocolErrorType(error_type);
- if (error_type == sync_pb::SyncEnums::CLEAR_PENDING ||
- error_type == sync_pb::SyncEnums::NOT_MY_BIRTHDAY ||
- error_type == sync_pb::SyncEnums::ENCRYPTION_OBSOLETE) {
- error.action = DISABLE_SYNC_ON_CLIENT;
- } else if (error_type == sync_pb::SyncEnums::CLIENT_DATA_OBSOLETE) {
- error.action = RESET_LOCAL_SYNC_DATA;
- } else if (error_type == sync_pb::SyncEnums::DISABLED_BY_ADMIN) {
- error.action = STOP_SYNC_FOR_DISABLED_ACCOUNT;
- } // There is no other action we can compute for legacy server.
- return error;
- }
- // Verifies the store birthday, alerting/resetting as appropriate if there's a
- // mismatch. Return false if the syncer should be stuck.
- bool ProcessResponseBirthday(const ClientToServerResponse& response,
- SyncCycleContext* context) {
- const std::string& local_birthday = context->birthday();
- if (local_birthday.empty()) {
- if (!response.has_store_birthday()) {
- DLOG(WARNING) << "Expected a birthday on first sync.";
- return false;
- }
- DVLOG(1) << "New store birthday: " << response.store_birthday();
- context->set_birthday(response.store_birthday());
- return true;
- }
- // Error situation, but we're not stuck.
- if (!response.has_store_birthday()) {
- DLOG(WARNING) << "No birthday in server response?";
- return true;
- }
- if (response.store_birthday() != local_birthday) {
- DLOG(WARNING) << "Birthday changed, showing syncer stuck";
- return false;
- }
- return true;
- }
- void SaveBagOfChipsFromResponse(const sync_pb::ClientToServerResponse& response,
- SyncCycleContext* context) {
- if (!response.has_new_bag_of_chips())
- return;
- std::string bag_of_chips;
- if (response.new_bag_of_chips().SerializeToString(&bag_of_chips))
- context->set_bag_of_chips(bag_of_chips);
- }
- } // namespace
- ModelTypeSet GetTypesToMigrate(const ClientToServerResponse& response) {
- ModelTypeSet to_migrate;
- for (int i = 0; i < response.migrated_data_type_id_size(); i++) {
- int field_number = response.migrated_data_type_id(i);
- ModelType model_type = GetModelTypeFromSpecificsFieldNumber(field_number);
- if (!IsRealDataType(model_type)) {
- DLOG(WARNING) << "Unknown field number " << field_number;
- continue;
- }
- to_migrate.Put(model_type);
- }
- return to_migrate;
- }
- SyncProtocolError ConvertErrorPBToSyncProtocolError(
- const sync_pb::ClientToServerResponse_Error& error) {
- SyncProtocolError sync_protocol_error;
- sync_protocol_error.error_type =
- PBErrorTypeToSyncProtocolErrorType(error.error_type());
- sync_protocol_error.error_description = error.error_description();
- sync_protocol_error.action = PBActionToClientAction(error.action());
- if (error.error_data_type_ids_size() > 0) {
- // THROTTLED and PARTIAL_FAILURE are currently the only error codes
- // that uses |error_data_types|.
- // In both cases, |error_data_types| are throttled.
- for (int i = 0; i < error.error_data_type_ids_size(); ++i) {
- int field_number = error.error_data_type_ids(i);
- ModelType model_type = GetModelTypeFromSpecificsFieldNumber(field_number);
- if (!IsRealDataType(model_type)) {
- DLOG(WARNING) << "Unknown field number " << field_number;
- continue;
- }
- sync_protocol_error.error_data_types.Put(model_type);
- }
- }
- return sync_protocol_error;
- }
- // static
- bool SyncerProtoUtil::IsSyncDisabledByAdmin(
- const sync_pb::ClientToServerResponse& response) {
- return (response.has_error_code() &&
- response.error_code() == sync_pb::SyncEnums::DISABLED_BY_ADMIN);
- }
- // static
- SyncProtocolError SyncerProtoUtil::GetProtocolErrorFromResponse(
- const sync_pb::ClientToServerResponse& response,
- SyncCycleContext* context) {
- SyncProtocolError sync_protocol_error;
- // The DISABLED_BY_ADMIN error overrides other errors sent by the server.
- if (IsSyncDisabledByAdmin(response)) {
- sync_protocol_error.error_type = DISABLED_BY_ADMIN;
- sync_protocol_error.action = STOP_SYNC_FOR_DISABLED_ACCOUNT;
- } else if (response.has_error()) {
- // If the server provides explicit error information, just honor it.
- sync_protocol_error = ConvertErrorPBToSyncProtocolError(response.error());
- } else if (!ProcessResponseBirthday(response, context)) {
- // If sync isn't disabled, first check for a birthday mismatch error.
- if (response.error_code() == sync_pb::SyncEnums::CLIENT_DATA_OBSOLETE) {
- // Server indicates that client needs to reset sync data.
- sync_protocol_error.error_type = CLIENT_DATA_OBSOLETE;
- sync_protocol_error.action = RESET_LOCAL_SYNC_DATA;
- } else {
- sync_protocol_error.error_type = NOT_MY_BIRTHDAY;
- sync_protocol_error.action = DISABLE_SYNC_ON_CLIENT;
- }
- } else {
- // Legacy server implementation. Compute the error based on |error_code|.
- sync_protocol_error = ErrorCodeToSyncProtocolError(response.error_code());
- }
- // Trivially inferred actions.
- if (sync_protocol_error.action == UNKNOWN_ACTION &&
- sync_protocol_error.error_type == ENCRYPTION_OBSOLETE) {
- sync_protocol_error.action = DISABLE_SYNC_ON_CLIENT;
- }
- return sync_protocol_error;
- }
- // static
- void SyncerProtoUtil::SetProtocolVersion(ClientToServerMessage* msg) {
- const int current_version =
- ClientToServerMessage::default_instance().protocol_version();
- msg->set_protocol_version(current_version);
- }
- // static
- bool SyncerProtoUtil::PostAndProcessHeaders(ServerConnectionManager* scm,
- const ClientToServerMessage& msg,
- ClientToServerResponse* response) {
- DCHECK(msg.has_protocol_version());
- DCHECK_EQ(msg.protocol_version(),
- ClientToServerMessage::default_instance().protocol_version());
- std::string buffer_in;
- msg.SerializeToString(&buffer_in);
- UMA_HISTOGRAM_ENUMERATION("Sync.PostedClientToServerMessage",
- msg.message_contents(),
- ClientToServerMessage::Contents_MAX + 1);
- std::map<int, std::string> progress_marker_token_per_data_type;
- if (msg.has_get_updates()) {
- UMA_HISTOGRAM_ENUMERATION("Sync.PostedGetUpdatesOrigin",
- msg.get_updates().get_updates_origin(),
- sync_pb::SyncEnums::GetUpdatesOrigin_ARRAYSIZE);
- for (const sync_pb::DataTypeProgressMarker& progress_marker :
- msg.get_updates().from_progress_marker()) {
- progress_marker_token_per_data_type[progress_marker.data_type_id()] =
- progress_marker.token();
- UMA_HISTOGRAM_ENUMERATION(
- "Sync.PostedDataTypeGetUpdatesRequest",
- ModelTypeHistogramValue(GetModelTypeFromSpecificsFieldNumber(
- progress_marker.data_type_id())));
- }
- }
- const base::Time start_time = base::Time::Now();
- // User-initiated sync messages should not be batched. GET_UPDATES messages
- // are mostly safe to consider non-user-initiated.
- // TODO(https://crbug.com/1293657): Confirm that treating GET_UPDATES as
- // non-user-initiated is reasonable. GET_UPDATES messages could be latency
- // sensitive since these requests most commonly happen because of some
- // user-initiated changes on a different device.
- bool allow_batching =
- msg.message_contents() == ClientToServerMessage::GET_UPDATES;
- // Fills in buffer_out.
- std::string buffer_out;
- HttpResponse http_response =
- scm->PostBufferWithCachedAuth(buffer_in, allow_batching, &buffer_out);
- if (http_response.server_status != HttpResponse::SERVER_CONNECTION_OK) {
- LOG(WARNING) << "Error posting from syncer:" << http_response;
- return false;
- }
- if (!response->ParseFromString(buffer_out)) {
- DLOG(WARNING) << "Error parsing response from sync server";
- return false;
- }
- UMA_HISTOGRAM_MEDIUM_TIMES("Sync.PostedClientToServerMessageLatency",
- base::Time::Now() - start_time);
- // The error can be specified in 2 different fields, so consider both of them.
- sync_pb::SyncEnums::ErrorType error_type =
- response->has_error() ? response->error().error_type()
- : response->error_code();
- if (error_type != sync_pb::SyncEnums::SUCCESS) {
- base::UmaHistogramSparse("Sync.PostedClientToServerMessageError2",
- error_type);
- }
- return true;
- }
- base::TimeDelta SyncerProtoUtil::GetThrottleDelay(
- const ClientToServerResponse& response) {
- base::TimeDelta throttle_delay = kSyncDelayAfterThrottled;
- if (response.has_client_command()) {
- const sync_pb::ClientCommand& command = response.client_command();
- if (command.has_throttle_delay_seconds()) {
- throttle_delay = base::Seconds(command.throttle_delay_seconds());
- }
- }
- return throttle_delay;
- }
- // static
- void SyncerProtoUtil::AddRequiredFieldsToClientToServerMessage(
- const SyncCycle* cycle,
- sync_pb::ClientToServerMessage* msg) {
- DCHECK(msg);
- SetProtocolVersion(msg);
- const std::string birthday = cycle->context()->birthday();
- if (!birthday.empty())
- msg->set_store_birthday(birthday);
- DCHECK(msg->has_store_birthday() || !IsBirthdayRequired(*msg));
- msg->mutable_bag_of_chips()->ParseFromString(
- cycle->context()->bag_of_chips());
- msg->set_api_key(google_apis::GetAPIKey());
- msg->mutable_client_status()->CopyFrom(cycle->context()->client_status());
- msg->set_invalidator_client_id(cycle->context()->invalidator_client_id());
- }
- // static
- SyncerError SyncerProtoUtil::PostClientToServerMessage(
- const ClientToServerMessage& msg,
- ClientToServerResponse* response,
- SyncCycle* cycle,
- ModelTypeSet* partial_failure_data_types) {
- DCHECK(response);
- DCHECK(msg.has_protocol_version());
- DCHECK(msg.has_store_birthday() || !IsBirthdayRequired(msg));
- DCHECK(msg.has_bag_of_chips());
- DCHECK(msg.has_api_key());
- DCHECK(msg.has_client_status());
- DCHECK(msg.has_invalidator_client_id());
- LogClientToServerMessage(msg);
- if (!PostAndProcessHeaders(cycle->context()->connection_manager(), msg,
- response)) {
- // There was an error establishing communication with the server.
- // We can not proceed beyond this point.
- const HttpResponse::ServerConnectionCode server_status =
- cycle->context()->connection_manager()->server_status();
- DCHECK_NE(server_status, HttpResponse::NONE);
- DCHECK_NE(server_status, HttpResponse::SERVER_CONNECTION_OK);
- return ServerConnectionErrorAsSyncerError(
- server_status, cycle->context()->connection_manager()->net_error_code(),
- cycle->context()->connection_manager()->http_status_code());
- }
- LogClientToServerResponse(*response);
- // Remember a bag of chips if it has been sent by the server.
- SaveBagOfChipsFromResponse(*response, cycle->context());
- SyncProtocolError sync_protocol_error =
- GetProtocolErrorFromResponse(*response, cycle->context());
- // Inform the delegate of the error we got.
- cycle->delegate()->OnSyncProtocolError(sync_protocol_error);
- // Update our state for any other commands we've received.
- if (response->has_client_command()) {
- const sync_pb::ClientCommand& command = response->client_command();
- if (command.has_max_commit_batch_size()) {
- cycle->context()->set_max_commit_batch_size(
- command.max_commit_batch_size());
- }
- if (command.has_set_sync_poll_interval()) {
- base::TimeDelta interval =
- base::Seconds(command.set_sync_poll_interval());
- if (interval.is_zero()) {
- DLOG(WARNING) << "Received zero poll interval from server. Ignoring.";
- } else {
- cycle->context()->set_poll_interval(interval);
- cycle->delegate()->OnReceivedPollIntervalUpdate(interval);
- }
- }
- if (command.has_sessions_commit_delay_seconds()) {
- std::map<ModelType, base::TimeDelta> delay_map;
- delay_map[SESSIONS] =
- base::Seconds(command.sessions_commit_delay_seconds());
- cycle->delegate()->OnReceivedCustomNudgeDelays(delay_map);
- }
- if (command.has_client_invalidation_hint_buffer_size()) {
- cycle->delegate()->OnReceivedClientInvalidationHintBufferSize(
- command.client_invalidation_hint_buffer_size());
- }
- if (command.has_gu_retry_delay_seconds()) {
- cycle->delegate()->OnReceivedGuRetryDelay(
- base::Seconds(command.gu_retry_delay_seconds()));
- }
- if (command.custom_nudge_delays_size() > 0) {
- // Note that because this happens after the sessions_commit_delay_seconds
- // handling, any SESSIONS value in this map will override the one in
- // sessions_commit_delay_seconds.
- std::map<ModelType, base::TimeDelta> delay_map;
- for (int i = 0; i < command.custom_nudge_delays_size(); ++i) {
- ModelType type = GetModelTypeFromSpecificsFieldNumber(
- command.custom_nudge_delays(i).datatype_id());
- if (type != UNSPECIFIED) {
- delay_map[type] =
- base::Milliseconds(command.custom_nudge_delays(i).delay_ms());
- }
- }
- cycle->delegate()->OnReceivedCustomNudgeDelays(delay_map);
- }
- absl::optional<int> max_tokens;
- if (command.has_extension_types_max_tokens()) {
- max_tokens = command.extension_types_max_tokens();
- }
- absl::optional<base::TimeDelta> refill_interval;
- if (command.has_extension_types_refill_interval_seconds()) {
- refill_interval =
- base::Seconds(command.extension_types_refill_interval_seconds());
- }
- absl::optional<base::TimeDelta> depleted_quota_nudge_delay;
- if (command.has_extension_types_depleted_quota_nudge_delay_seconds()) {
- depleted_quota_nudge_delay = base::Seconds(
- command.extension_types_depleted_quota_nudge_delay_seconds());
- }
- if (max_tokens || refill_interval || depleted_quota_nudge_delay) {
- cycle->delegate()->OnReceivedQuotaParamsForExtensionTypes(
- max_tokens, refill_interval, depleted_quota_nudge_delay);
- }
- }
- // Now do any special handling for the error type and decide on the return
- // value.
- switch (sync_protocol_error.error_type) {
- case UNKNOWN_ERROR:
- LOG(WARNING) << "Sync protocol out-of-date. The server is using a more "
- << "recent version.";
- return SyncerError(SyncerError::SERVER_RETURN_UNKNOWN_ERROR);
- case SYNC_SUCCESS:
- return SyncerError(SyncerError::SYNCER_OK);
- case THROTTLED:
- if (sync_protocol_error.error_data_types.Empty()) {
- DLOG(WARNING) << "Client fully throttled by syncer.";
- cycle->delegate()->OnThrottled(GetThrottleDelay(*response));
- } else {
- // This is a special case, since server only throttle some of datatype,
- // so can treat this case as partial failure.
- DLOG(WARNING) << "Some types throttled by syncer.";
- cycle->delegate()->OnTypesThrottled(
- sync_protocol_error.error_data_types, GetThrottleDelay(*response));
- if (partial_failure_data_types != nullptr) {
- *partial_failure_data_types = sync_protocol_error.error_data_types;
- }
- return SyncerError(SyncerError::SYNCER_OK);
- }
- return SyncerError(SyncerError::SERVER_RETURN_THROTTLED);
- case TRANSIENT_ERROR:
- return SyncerError(SyncerError::SERVER_RETURN_TRANSIENT_ERROR);
- case MIGRATION_DONE:
- LOG_IF(ERROR, 0 >= response->migrated_data_type_id_size())
- << "MIGRATION_DONE but no types specified.";
- cycle->delegate()->OnReceivedMigrationRequest(
- GetTypesToMigrate(*response));
- return SyncerError(SyncerError::SERVER_RETURN_MIGRATION_DONE);
- case CLEAR_PENDING:
- return SyncerError(SyncerError::SERVER_RETURN_CLEAR_PENDING);
- case NOT_MY_BIRTHDAY:
- return SyncerError(SyncerError::SERVER_RETURN_NOT_MY_BIRTHDAY);
- case DISABLED_BY_ADMIN:
- return SyncerError(SyncerError::SERVER_RETURN_DISABLED_BY_ADMIN);
- case PARTIAL_FAILURE:
- // This only happens when partial backoff during GetUpdates.
- if (!sync_protocol_error.error_data_types.Empty()) {
- DLOG(WARNING)
- << "Some types got partial failure by syncer during GetUpdates.";
- cycle->delegate()->OnTypesBackedOff(
- sync_protocol_error.error_data_types);
- }
- if (partial_failure_data_types != nullptr) {
- *partial_failure_data_types = sync_protocol_error.error_data_types;
- }
- return SyncerError(SyncerError::SYNCER_OK);
- case CLIENT_DATA_OBSOLETE:
- return SyncerError(SyncerError::SERVER_RETURN_CLIENT_DATA_OBSOLETE);
- case ENCRYPTION_OBSOLETE:
- return SyncerError(SyncerError::SERVER_RETURN_ENCRYPTION_OBSOLETE);
- }
- NOTREACHED();
- return SyncerError();
- }
- // static
- bool SyncerProtoUtil::ShouldMaintainPosition(
- const sync_pb::SyncEntity& sync_entity) {
- // Maintain positions for bookmarks that are not server-defined top-level
- // folders.
- return GetModelTypeFromSpecifics(sync_entity.specifics()) == BOOKMARKS &&
- !(sync_entity.folder() &&
- !sync_entity.server_defined_unique_tag().empty());
- }
- // static
- bool SyncerProtoUtil::ShouldMaintainHierarchy(
- const sync_pb::SyncEntity& sync_entity) {
- // Maintain hierarchy for bookmarks or top-level items.
- return GetModelTypeFromSpecifics(sync_entity.specifics()) == BOOKMARKS ||
- sync_entity.parent_id_string() == "0";
- }
- std::string SyncerProtoUtil::SyncEntityDebugString(
- const sync_pb::SyncEntity& entry) {
- const std::string& mtime_str =
- GetTimeDebugString(ProtoTimeToTime(entry.mtime()));
- const std::string& ctime_str =
- GetTimeDebugString(ProtoTimeToTime(entry.ctime()));
- return base::StringPrintf(
- "id: %s, parent_id: %s, "
- "version: %" PRId64
- "d, "
- "mtime: %" PRId64
- "d (%s), "
- "ctime: %" PRId64
- "d (%s), "
- "name: %s, "
- "d, "
- "%s ",
- entry.id_string().c_str(), entry.parent_id_string().c_str(),
- entry.version(), entry.mtime(), mtime_str.c_str(), entry.ctime(),
- ctime_str.c_str(), entry.name().c_str(),
- entry.deleted() ? "deleted, " : "");
- }
- namespace {
- std::string GetUpdatesResponseString(
- const sync_pb::GetUpdatesResponse& response) {
- std::string output;
- output.append("GetUpdatesResponse:\n");
- for (int i = 0; i < response.entries_size(); i++) {
- output.append(SyncerProtoUtil::SyncEntityDebugString(response.entries(i)));
- output.append("\n");
- }
- return output;
- }
- } // namespace
- std::string SyncerProtoUtil::ClientToServerResponseDebugString(
- const ClientToServerResponse& response) {
- // Add more handlers as needed.
- std::string output;
- if (response.has_get_updates())
- output.append(GetUpdatesResponseString(response.get_updates()));
- return output;
- }
- } // namespace syncer
|