| Index: components/invalidation/gcm_network_channel.cc
|
| diff --git a/components/invalidation/gcm_network_channel.cc b/components/invalidation/gcm_network_channel.cc
|
| deleted file mode 100644
|
| index 0c5361261361d875380c75c9afdf6c068fcc1efe..0000000000000000000000000000000000000000
|
| --- a/components/invalidation/gcm_network_channel.cc
|
| +++ /dev/null
|
| @@ -1,459 +0,0 @@
|
| -// Copyright 2014 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 "base/base64.h"
|
| -#include "base/i18n/time_formatting.h"
|
| -#include "base/location.h"
|
| -#include "base/metrics/histogram_macros.h"
|
| -#include "base/sha1.h"
|
| -#include "base/single_thread_task_runner.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/thread_task_runner_handle.h"
|
| -#if !defined(OS_ANDROID)
|
| -// channel_common.proto defines ANDROID constant that conflicts with Android
|
| -// build. At the same time TiclInvalidationService is not used on Android so it
|
| -// is safe to exclude these protos from Android build.
|
| -#include "google/cacheinvalidation/android_channel.pb.h"
|
| -#include "google/cacheinvalidation/channel_common.pb.h"
|
| -#include "google/cacheinvalidation/types.pb.h"
|
| -#endif
|
| -#include "components/invalidation/gcm_network_channel.h"
|
| -#include "components/invalidation/gcm_network_channel_delegate.h"
|
| -#include "google_apis/gaia/google_service_auth_error.h"
|
| -#include "net/http/http_status_code.h"
|
| -#include "net/url_request/url_fetcher.h"
|
| -#include "net/url_request/url_request_status.h"
|
| -
|
| -namespace syncer {
|
| -
|
| -namespace {
|
| -
|
| -const char kCacheInvalidationEndpointUrl[] =
|
| - "https://clients4.google.com/invalidation/android/request/";
|
| -const char kCacheInvalidationPackageName[] = "com.google.chrome.invalidations";
|
| -
|
| -// Register backoff policy.
|
| -const net::BackoffEntry::Policy kRegisterBackoffPolicy = {
|
| - // Number of initial errors (in sequence) to ignore before applying
|
| - // exponential back-off rules.
|
| - 0,
|
| -
|
| - // Initial delay for exponential back-off in ms.
|
| - 2000, // 2 seconds.
|
| -
|
| - // Factor by which the waiting time will be multiplied.
|
| - 2,
|
| -
|
| - // Fuzzing percentage. ex: 10% will spread requests randomly
|
| - // between 90%-100% of the calculated time.
|
| - 0.2, // 20%.
|
| -
|
| - // Maximum amount of time we are willing to delay our request in ms.
|
| - 1000 * 3600 * 4, // 4 hours.
|
| -
|
| - // Time to keep an entry from being discarded even when it
|
| - // has no significant state, -1 to never discard.
|
| - -1,
|
| -
|
| - // Don't use initial delay unless the last request was an error.
|
| - false,
|
| -};
|
| -
|
| -// Incoming message status values for UMA_HISTOGRAM.
|
| -enum IncomingMessageStatus {
|
| - INCOMING_MESSAGE_SUCCESS,
|
| - MESSAGE_EMPTY, // GCM message's content is missing or empty.
|
| - INVALID_ENCODING, // Base64Decode failed.
|
| - INVALID_PROTO, // Parsing protobuf failed.
|
| -
|
| - // This enum is used in UMA_HISTOGRAM_ENUMERATION. Insert new values above
|
| - // this line.
|
| - INCOMING_MESSAGE_STATUS_COUNT
|
| -};
|
| -
|
| -// Outgoing message status values for UMA_HISTOGRAM.
|
| -enum OutgoingMessageStatus {
|
| - OUTGOING_MESSAGE_SUCCESS,
|
| - MESSAGE_DISCARDED, // New message started before old one was sent.
|
| - ACCESS_TOKEN_FAILURE, // Requeting access token failed.
|
| - POST_FAILURE, // HTTP Post failed.
|
| -
|
| - // This enum is used in UMA_HISTOGRAM_ENUMERATION. Insert new values above
|
| - // this line.
|
| - OUTGOING_MESSAGE_STATUS_COUNT
|
| -};
|
| -
|
| -const char kIncomingMessageStatusHistogram[] =
|
| - "GCMInvalidations.IncomingMessageStatus";
|
| -const char kOutgoingMessageStatusHistogram[] =
|
| - "GCMInvalidations.OutgoingMessageStatus";
|
| -
|
| -void RecordIncomingMessageStatus(IncomingMessageStatus status) {
|
| - UMA_HISTOGRAM_ENUMERATION(kIncomingMessageStatusHistogram,
|
| - status,
|
| - INCOMING_MESSAGE_STATUS_COUNT);
|
| -}
|
| -
|
| -void RecordOutgoingMessageStatus(OutgoingMessageStatus status) {
|
| - UMA_HISTOGRAM_ENUMERATION(kOutgoingMessageStatusHistogram,
|
| - status,
|
| - OUTGOING_MESSAGE_STATUS_COUNT);
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -GCMNetworkChannel::GCMNetworkChannel(
|
| - scoped_refptr<net::URLRequestContextGetter> request_context_getter,
|
| - scoped_ptr<GCMNetworkChannelDelegate> delegate)
|
| - : request_context_getter_(request_context_getter),
|
| - delegate_(delegate.Pass()),
|
| - register_backoff_entry_(new net::BackoffEntry(&kRegisterBackoffPolicy)),
|
| - gcm_channel_online_(false),
|
| - http_channel_online_(false),
|
| - diagnostic_info_(this),
|
| - weak_factory_(this) {
|
| - net::NetworkChangeNotifier::AddNetworkChangeObserver(this);
|
| - delegate_->Initialize(base::Bind(&GCMNetworkChannel::OnConnectionStateChanged,
|
| - weak_factory_.GetWeakPtr()));
|
| - Register();
|
| -}
|
| -
|
| -GCMNetworkChannel::~GCMNetworkChannel() {
|
| - net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
|
| -}
|
| -
|
| -void GCMNetworkChannel::Register() {
|
| - delegate_->Register(base::Bind(&GCMNetworkChannel::OnRegisterComplete,
|
| - weak_factory_.GetWeakPtr()));
|
| -}
|
| -
|
| -void GCMNetworkChannel::OnRegisterComplete(
|
| - const std::string& registration_id,
|
| - gcm::GCMClient::Result result) {
|
| - DCHECK(CalledOnValidThread());
|
| - if (result == gcm::GCMClient::SUCCESS) {
|
| - DCHECK(!registration_id.empty());
|
| - DVLOG(2) << "Got registration_id";
|
| - register_backoff_entry_->Reset();
|
| - registration_id_ = registration_id;
|
| - if (!cached_message_.empty())
|
| - RequestAccessToken();
|
| - } else {
|
| - DVLOG(2) << "Register failed: " << result;
|
| - // Retry in case of transient error.
|
| - switch (result) {
|
| - case gcm::GCMClient::NETWORK_ERROR:
|
| - case gcm::GCMClient::SERVER_ERROR:
|
| - case gcm::GCMClient::TTL_EXCEEDED:
|
| - case gcm::GCMClient::UNKNOWN_ERROR: {
|
| - register_backoff_entry_->InformOfRequest(false);
|
| - base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
| - FROM_HERE, base::Bind(&GCMNetworkChannel::Register,
|
| - weak_factory_.GetWeakPtr()),
|
| - register_backoff_entry_->GetTimeUntilRelease());
|
| - break;
|
| - }
|
| - default:
|
| - break;
|
| - }
|
| - }
|
| - diagnostic_info_.registration_id_ = registration_id_;
|
| - diagnostic_info_.registration_result_ = result;
|
| -}
|
| -
|
| -void GCMNetworkChannel::SendMessage(const std::string& message) {
|
| - DCHECK(CalledOnValidThread());
|
| - DCHECK(!message.empty());
|
| - DVLOG(2) << "SendMessage";
|
| - diagnostic_info_.sent_messages_count_++;
|
| - if (!cached_message_.empty()) {
|
| - RecordOutgoingMessageStatus(MESSAGE_DISCARDED);
|
| - }
|
| - cached_message_ = message;
|
| -
|
| - if (!registration_id_.empty()) {
|
| - RequestAccessToken();
|
| - }
|
| -}
|
| -
|
| -void GCMNetworkChannel::RequestAccessToken() {
|
| - DCHECK(CalledOnValidThread());
|
| - delegate_->RequestToken(base::Bind(&GCMNetworkChannel::OnGetTokenComplete,
|
| - weak_factory_.GetWeakPtr()));
|
| -}
|
| -
|
| -void GCMNetworkChannel::OnGetTokenComplete(
|
| - const GoogleServiceAuthError& error,
|
| - const std::string& token) {
|
| - DCHECK(CalledOnValidThread());
|
| - if (cached_message_.empty()) {
|
| - // Nothing to do.
|
| - return;
|
| - }
|
| -
|
| - if (error.state() != GoogleServiceAuthError::NONE) {
|
| - // Requesting access token failed. Persistent errors will be reported by
|
| - // token service. Just drop this request, cacheinvalidations will retry
|
| - // sending message and at that time we'll retry requesting access token.
|
| - DVLOG(1) << "RequestAccessToken failed: " << error.ToString();
|
| - RecordOutgoingMessageStatus(ACCESS_TOKEN_FAILURE);
|
| - // Message won't get sent. Notify that http channel doesn't work.
|
| - UpdateHttpChannelState(false);
|
| - cached_message_.clear();
|
| - return;
|
| - }
|
| - DCHECK(!token.empty());
|
| - // Save access token in case POST fails and we need to invalidate it.
|
| - access_token_ = token;
|
| -
|
| - DVLOG(2) << "Got access token, sending message";
|
| - fetcher_ = net::URLFetcher::Create(BuildUrl(registration_id_),
|
| - net::URLFetcher::POST, this);
|
| - fetcher_->SetRequestContext(request_context_getter_.get());
|
| - const std::string auth_header("Authorization: Bearer " + access_token_);
|
| - fetcher_->AddExtraRequestHeader(auth_header);
|
| - if (!echo_token_.empty()) {
|
| - const std::string echo_header("echo-token: " + echo_token_);
|
| - fetcher_->AddExtraRequestHeader(echo_header);
|
| - }
|
| - fetcher_->SetUploadData("application/x-protobuffer", cached_message_);
|
| - fetcher_->Start();
|
| - // Clear message to prevent accidentally resending it in the future.
|
| - cached_message_.clear();
|
| -}
|
| -
|
| -void GCMNetworkChannel::OnURLFetchComplete(const net::URLFetcher* source) {
|
| - DCHECK(CalledOnValidThread());
|
| - DCHECK_EQ(fetcher_, source);
|
| - // Free fetcher at the end of function.
|
| - scoped_ptr<net::URLFetcher> fetcher = fetcher_.Pass();
|
| -
|
| - net::URLRequestStatus status = fetcher->GetStatus();
|
| - diagnostic_info_.last_post_response_code_ =
|
| - status.is_success() ? source->GetResponseCode() : status.error();
|
| -
|
| - if (status.is_success() &&
|
| - fetcher->GetResponseCode() == net::HTTP_UNAUTHORIZED) {
|
| - DVLOG(1) << "URLFetcher failure: HTTP_UNAUTHORIZED";
|
| - delegate_->InvalidateToken(access_token_);
|
| - }
|
| -
|
| - if (!status.is_success() ||
|
| - (fetcher->GetResponseCode() != net::HTTP_OK &&
|
| - fetcher->GetResponseCode() != net::HTTP_NO_CONTENT)) {
|
| - DVLOG(1) << "URLFetcher failure";
|
| - RecordOutgoingMessageStatus(POST_FAILURE);
|
| - // POST failed. Notify that http channel doesn't work.
|
| - UpdateHttpChannelState(false);
|
| - return;
|
| - }
|
| -
|
| - RecordOutgoingMessageStatus(OUTGOING_MESSAGE_SUCCESS);
|
| - // Successfully sent message. Http channel works.
|
| - UpdateHttpChannelState(true);
|
| - DVLOG(2) << "URLFetcher success";
|
| -}
|
| -
|
| -void GCMNetworkChannel::OnIncomingMessage(const std::string& message,
|
| - const std::string& echo_token) {
|
| -#if !defined(OS_ANDROID)
|
| - if (!echo_token.empty())
|
| - echo_token_ = echo_token;
|
| - diagnostic_info_.last_message_empty_echo_token_ = echo_token.empty();
|
| - diagnostic_info_.last_message_received_time_ = base::Time::Now();
|
| -
|
| - if (message.empty()) {
|
| - RecordIncomingMessageStatus(MESSAGE_EMPTY);
|
| - return;
|
| - }
|
| - std::string data;
|
| - if (!Base64DecodeURLSafe(message, &data)) {
|
| - RecordIncomingMessageStatus(INVALID_ENCODING);
|
| - return;
|
| - }
|
| - ipc::invalidation::AddressedAndroidMessage android_message;
|
| - if (!android_message.ParseFromString(data) ||
|
| - !android_message.has_message()) {
|
| - RecordIncomingMessageStatus(INVALID_PROTO);
|
| - return;
|
| - }
|
| - DVLOG(2) << "Deliver incoming message";
|
| - RecordIncomingMessageStatus(INCOMING_MESSAGE_SUCCESS);
|
| - UpdateGcmChannelState(true);
|
| - DeliverIncomingMessage(android_message.message());
|
| -#else
|
| - // This code shouldn't be invoked on Android.
|
| - NOTREACHED();
|
| -#endif
|
| -}
|
| -
|
| -void GCMNetworkChannel::OnConnectionStateChanged(bool online) {
|
| - UpdateGcmChannelState(online);
|
| -}
|
| -
|
| -void GCMNetworkChannel::OnNetworkChanged(
|
| - net::NetworkChangeNotifier::ConnectionType connection_type) {
|
| - // Network connection is restored. Let's notify cacheinvalidations so it has
|
| - // chance to retry.
|
| - NotifyNetworkStatusChange(
|
| - connection_type != net::NetworkChangeNotifier::CONNECTION_NONE);
|
| -}
|
| -
|
| -void GCMNetworkChannel::UpdateGcmChannelState(bool online) {
|
| - if (gcm_channel_online_ == online)
|
| - return;
|
| - gcm_channel_online_ = online;
|
| - InvalidatorState channel_state = TRANSIENT_INVALIDATION_ERROR;
|
| - if (gcm_channel_online_ && http_channel_online_)
|
| - channel_state = INVALIDATIONS_ENABLED;
|
| - NotifyChannelStateChange(channel_state);
|
| -}
|
| -
|
| -void GCMNetworkChannel::UpdateHttpChannelState(bool online) {
|
| - if (http_channel_online_ == online)
|
| - return;
|
| - http_channel_online_ = online;
|
| - InvalidatorState channel_state = TRANSIENT_INVALIDATION_ERROR;
|
| - if (gcm_channel_online_ && http_channel_online_)
|
| - channel_state = INVALIDATIONS_ENABLED;
|
| - NotifyChannelStateChange(channel_state);
|
| -}
|
| -
|
| -GURL GCMNetworkChannel::BuildUrl(const std::string& registration_id) {
|
| - DCHECK(!registration_id.empty());
|
| -
|
| -#if !defined(OS_ANDROID)
|
| - ipc::invalidation::EndpointId endpoint_id;
|
| - endpoint_id.set_c2dm_registration_id(registration_id);
|
| - endpoint_id.set_client_key(std::string());
|
| - endpoint_id.set_package_name(kCacheInvalidationPackageName);
|
| - endpoint_id.mutable_channel_version()->set_major_version(
|
| - ipc::invalidation::INITIAL);
|
| - std::string endpoint_id_buffer;
|
| - endpoint_id.SerializeToString(&endpoint_id_buffer);
|
| -
|
| - ipc::invalidation::NetworkEndpointId network_endpoint_id;
|
| - network_endpoint_id.set_network_address(
|
| - ipc::invalidation::NetworkEndpointId_NetworkAddress_ANDROID);
|
| - network_endpoint_id.set_client_address(endpoint_id_buffer);
|
| - std::string network_endpoint_id_buffer;
|
| - network_endpoint_id.SerializeToString(&network_endpoint_id_buffer);
|
| -
|
| - std::string base64URLPiece;
|
| - Base64EncodeURLSafe(network_endpoint_id_buffer, &base64URLPiece);
|
| -
|
| - std::string url(kCacheInvalidationEndpointUrl);
|
| - url += base64URLPiece;
|
| - return GURL(url);
|
| -#else
|
| - // This code shouldn't be invoked on Android.
|
| - NOTREACHED();
|
| - return GURL();
|
| -#endif
|
| -}
|
| -
|
| -void GCMNetworkChannel::Base64EncodeURLSafe(const std::string& input,
|
| - std::string* output) {
|
| - base::Base64Encode(input, output);
|
| - // Covert to url safe alphabet.
|
| - base::ReplaceChars(*output, "+", "-", output);
|
| - base::ReplaceChars(*output, "/", "_", output);
|
| - // Trim padding.
|
| - size_t padding_size = 0;
|
| - for (size_t i = output->size(); i > 0 && (*output)[i - 1] == '='; --i)
|
| - ++padding_size;
|
| - output->resize(output->size() - padding_size);
|
| -}
|
| -
|
| -bool GCMNetworkChannel::Base64DecodeURLSafe(const std::string& input,
|
| - std::string* output) {
|
| - // Add padding.
|
| - size_t padded_size = (input.size() + 3) - (input.size() + 3) % 4;
|
| - std::string padded_input(input);
|
| - padded_input.resize(padded_size, '=');
|
| - // Convert to standard base64 alphabet.
|
| - base::ReplaceChars(padded_input, "-", "+", &padded_input);
|
| - base::ReplaceChars(padded_input, "_", "/", &padded_input);
|
| - return base::Base64Decode(padded_input, output);
|
| -}
|
| -
|
| -void GCMNetworkChannel::SetMessageReceiver(
|
| - invalidation::MessageCallback* incoming_receiver) {
|
| - delegate_->SetMessageReceiver(base::Bind(
|
| - &GCMNetworkChannel::OnIncomingMessage, weak_factory_.GetWeakPtr()));
|
| - SyncNetworkChannel::SetMessageReceiver(incoming_receiver);
|
| -}
|
| -
|
| -void GCMNetworkChannel::RequestDetailedStatus(
|
| - base::Callback<void(const base::DictionaryValue&)> callback) {
|
| - callback.Run(*diagnostic_info_.CollectDebugData());
|
| -}
|
| -
|
| -void GCMNetworkChannel::UpdateCredentials(const std::string& email,
|
| - const std::string& token) {
|
| - // Do nothing. We get access token by requesting it for every message.
|
| -}
|
| -
|
| -int GCMNetworkChannel::GetInvalidationClientType() {
|
| -#if defined(OS_IOS)
|
| - return ipc::invalidation::ClientType::CHROME_SYNC_GCM_IOS;
|
| -#else
|
| - return ipc::invalidation::ClientType::CHROME_SYNC_GCM_DESKTOP;
|
| -#endif
|
| -}
|
| -
|
| -void GCMNetworkChannel::ResetRegisterBackoffEntryForTest(
|
| - const net::BackoffEntry::Policy* policy) {
|
| - register_backoff_entry_.reset(new net::BackoffEntry(policy));
|
| -}
|
| -
|
| -GCMNetworkChannelDiagnostic::GCMNetworkChannelDiagnostic(
|
| - GCMNetworkChannel* parent)
|
| - : parent_(parent),
|
| - last_message_empty_echo_token_(false),
|
| - last_post_response_code_(0),
|
| - registration_result_(gcm::GCMClient::UNKNOWN_ERROR),
|
| - sent_messages_count_(0) {}
|
| -
|
| -scoped_ptr<base::DictionaryValue>
|
| -GCMNetworkChannelDiagnostic::CollectDebugData() const {
|
| - scoped_ptr<base::DictionaryValue> status(new base::DictionaryValue);
|
| - status->SetString("GCMNetworkChannel.Channel", "GCM");
|
| - std::string reg_id_hash = base::SHA1HashString(registration_id_);
|
| - status->SetString("GCMNetworkChannel.HashedRegistrationID",
|
| - base::HexEncode(reg_id_hash.c_str(), reg_id_hash.size()));
|
| - status->SetString("GCMNetworkChannel.RegistrationResult",
|
| - GCMClientResultToString(registration_result_));
|
| - status->SetBoolean("GCMNetworkChannel.HadLastMessageEmptyEchoToken",
|
| - last_message_empty_echo_token_);
|
| - status->SetString(
|
| - "GCMNetworkChannel.LastMessageReceivedTime",
|
| - base::TimeFormatShortDateAndTime(last_message_received_time_));
|
| - status->SetInteger("GCMNetworkChannel.LastPostResponseCode",
|
| - last_post_response_code_);
|
| - status->SetInteger("GCMNetworkChannel.SentMessages", sent_messages_count_);
|
| - status->SetInteger("GCMNetworkChannel.ReceivedMessages",
|
| - parent_->GetReceivedMessagesCount());
|
| - return status.Pass();
|
| -}
|
| -
|
| -std::string GCMNetworkChannelDiagnostic::GCMClientResultToString(
|
| - const gcm::GCMClient::Result result) const {
|
| -#define ENUM_CASE(x) case x: return #x; break;
|
| - switch (result) {
|
| - ENUM_CASE(gcm::GCMClient::SUCCESS);
|
| - ENUM_CASE(gcm::GCMClient::NETWORK_ERROR);
|
| - ENUM_CASE(gcm::GCMClient::SERVER_ERROR);
|
| - ENUM_CASE(gcm::GCMClient::TTL_EXCEEDED);
|
| - ENUM_CASE(gcm::GCMClient::UNKNOWN_ERROR);
|
| - ENUM_CASE(gcm::GCMClient::INVALID_PARAMETER);
|
| - ENUM_CASE(gcm::GCMClient::ASYNC_OPERATION_PENDING);
|
| - ENUM_CASE(gcm::GCMClient::GCM_DISABLED);
|
| - }
|
| - NOTREACHED();
|
| - return "";
|
| -}
|
| -
|
| -} // namespace syncer
|
|
|