diff options
Diffstat (limited to 'google_apis')
-rw-r--r-- | google_apis/gcm/engine/checkin_request.cc | 62 | ||||
-rw-r--r-- | google_apis/gcm/engine/checkin_request.h | 10 | ||||
-rw-r--r-- | google_apis/gcm/engine/checkin_request_unittest.cc | 137 | ||||
-rw-r--r-- | google_apis/gcm/gcm_client_impl.cc | 4 | ||||
-rw-r--r-- | google_apis/gcm/tools/mcs_probe.cc | 1 |
5 files changed, 187 insertions, 27 deletions
diff --git a/google_apis/gcm/engine/checkin_request.cc b/google_apis/gcm/engine/checkin_request.cc index 9f3159a..b46415a 100644 --- a/google_apis/gcm/engine/checkin_request.cc +++ b/google_apis/gcm/engine/checkin_request.cc @@ -22,6 +22,7 @@ const int kRequestVersionValue = 2; CheckinRequest::CheckinRequest( const CheckinRequestCallback& callback, + const net::BackoffEntry::Policy& backoff_policy, const checkin_proto::ChromeBuildProto& chrome_build_proto, int64 user_serial_number, uint64 android_id, @@ -29,10 +30,13 @@ CheckinRequest::CheckinRequest( net::URLRequestContextGetter* request_context_getter) : request_context_getter_(request_context_getter), callback_(callback), + backoff_entry_(&backoff_policy), chrome_build_proto_(chrome_build_proto), android_id_(android_id), security_token_(security_token), - user_serial_number_(user_serial_number) {} + user_serial_number_(user_serial_number), + weak_ptr_factory_(this) { +} CheckinRequest::~CheckinRequest() {} @@ -53,7 +57,6 @@ void CheckinRequest::Start() { checkin->set_type(checkin_proto::DEVICE_CHROME_BROWSER); #endif - std::string upload_data; CHECK(request.SerializeToString(&upload_data)); @@ -64,18 +67,55 @@ void CheckinRequest::Start() { url_fetcher_->Start(); } +void CheckinRequest::RetryWithBackoff(bool update_backoff) { + if (update_backoff) { + backoff_entry_.InformOfRequest(false); + url_fetcher_.reset(); + } + + if (backoff_entry_.ShouldRejectRequest()) { + DVLOG(1) << "Delay GCM checkin for: " + << backoff_entry_.GetTimeUntilRelease().InMilliseconds() + << " milliseconds."; + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&CheckinRequest::RetryWithBackoff, + weak_ptr_factory_.GetWeakPtr(), + false), + backoff_entry_.GetTimeUntilRelease()); + return; + } + + Start(); +} + void CheckinRequest::OnURLFetchComplete(const net::URLFetcher* source) { std::string response_string; checkin_proto::AndroidCheckinResponse response_proto; - if (!source->GetStatus().is_success() || - source->GetResponseCode() != net::HTTP_OK || + if (!source->GetStatus().is_success()) { + LOG(ERROR) << "Failed to get checkin response. Fetcher failed. Retrying."; + RetryWithBackoff(true); + return; + } + + net::HttpStatusCode response_status = static_cast<net::HttpStatusCode>( + source->GetResponseCode()); + if (response_status == net::HTTP_BAD_REQUEST || + response_status == net::HTTP_UNAUTHORIZED) { + // BAD_REQUEST indicates that the request was malformed. + // UNAUTHORIZED indicates that security token didn't match the android id. + LOG(ERROR) << "No point retrying the checkin with status: " + << response_status << ". Checkin failed."; + callback_.Run(0,0); + return; + } + + if (response_status != net::HTTP_OK || !source->GetResponseAsString(&response_string) || !response_proto.ParseFromString(response_string)) { - LOG(ERROR) << "Failed to get checkin response: " - << source->GetStatus().is_success() << " " - << source->GetResponseCode(); - // TODO(fgorski): Handle retry logic for certain responses. - callback_.Run(0, 0); + LOG(ERROR) << "Failed to get checkin response. HTTP Status: " + << response_status << ". Retrying."; + RetryWithBackoff(true); return; } @@ -83,8 +123,8 @@ void CheckinRequest::OnURLFetchComplete(const net::URLFetcher* source) { !response_proto.has_security_token() || response_proto.android_id() == 0 || response_proto.security_token() == 0) { - LOG(ERROR) << "Badly formatted checkin response."; - callback_.Run(0, 0); + LOG(ERROR) << "Android ID or security token is 0. Retrying."; + RetryWithBackoff(true); return; } diff --git a/google_apis/gcm/engine/checkin_request.h b/google_apis/gcm/engine/checkin_request.h index 8ea6ad9..548f0b8 100644 --- a/google_apis/gcm/engine/checkin_request.h +++ b/google_apis/gcm/engine/checkin_request.h @@ -7,8 +7,10 @@ #include "base/basictypes.h" #include "base/callback.h" +#include "base/memory/weak_ptr.h" #include "google_apis/gcm/base/gcm_export.h" #include "google_apis/gcm/protocol/android_checkin.pb.h" +#include "net/base/backoff_entry.h" #include "net/url_request/url_fetcher_delegate.h" namespace net { @@ -29,6 +31,7 @@ class GCM_EXPORT CheckinRequest : public net::URLFetcherDelegate { CheckinRequestCallback; CheckinRequest(const CheckinRequestCallback& callback, + const net::BackoffEntry::Policy& backoff_policy, const checkin_proto::ChromeBuildProto& chrome_build_proto, int64 user_serial_number, uint64 android_id, @@ -42,15 +45,22 @@ class GCM_EXPORT CheckinRequest : public net::URLFetcherDelegate { virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; private: + // Schedules a retry attempt, informs the backoff of a previous request's + // failure when |update_backoff| is true. + void RetryWithBackoff(bool update_backoff); + net::URLRequestContextGetter* request_context_getter_; CheckinRequestCallback callback_; + net::BackoffEntry backoff_entry_; scoped_ptr<net::URLFetcher> url_fetcher_; checkin_proto::ChromeBuildProto chrome_build_proto_; uint64 android_id_; uint64 security_token_; int64 user_serial_number_; + base::WeakPtrFactory<CheckinRequest> weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(CheckinRequest); }; diff --git a/google_apis/gcm/engine/checkin_request_unittest.cc b/google_apis/gcm/engine/checkin_request_unittest.cc index 1a42356..b618bbf 100644 --- a/google_apis/gcm/engine/checkin_request_unittest.cc +++ b/google_apis/gcm/engine/checkin_request_unittest.cc @@ -6,12 +6,45 @@ #include "google_apis/gcm/engine/checkin_request.h" #include "google_apis/gcm/protocol/checkin.pb.h" +#include "net/base/backoff_entry.h" #include "net/url_request/test_url_fetcher_factory.h" #include "net/url_request/url_request_test_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace gcm { +namespace { + +const net::BackoffEntry::Policy kDefaultBackoffPolicy = { + // Number of initial errors (in sequence) to ignore before applying + // exponential back-off rules. + // Explicitly set to 1 to skip the delay of the first Retry, as we are not + // trying to test the backoff itself, but rather the fact that retry happens. + 1, + + // Initial delay for exponential back-off in ms. + 15000, // 15 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.5, // 50%. + + // Maximum amount of time we are willing to delay our request in ms. + 1000 * 60 * 5, // 5 minutes. + + // 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, +}; + +} + const uint64 kAndroidId = 42UL; const uint64 kBlankAndroidId = 999999UL; const uint64 kBlankSecurityToken = 999999UL; @@ -45,6 +78,7 @@ class CheckinRequestTest : public testing::Test { void SetResponse(ResponseScenario response_scenario); protected: + bool callback_called_; uint64 android_id_; uint64 security_token_; int checkin_device_type_; @@ -56,7 +90,8 @@ class CheckinRequestTest : public testing::Test { }; CheckinRequestTest::CheckinRequestTest() - : android_id_(kBlankAndroidId), + : callback_called_(false), + android_id_(kBlankAndroidId), security_token_(kBlankSecurityToken), checkin_device_type_(0), url_request_context_getter_(new net::TestURLRequestContextGetter( @@ -66,6 +101,7 @@ CheckinRequestTest::~CheckinRequestTest() {} void CheckinRequestTest::FetcherCallback(uint64 android_id, uint64 security_token) { + callback_called_ = true; android_id_ = android_id; security_token_ = security_token; } @@ -83,6 +119,7 @@ void CheckinRequestTest::CreateRequest(uint64 android_id, request_.reset(new CheckinRequest( base::Bind(&CheckinRequestTest::FetcherCallback, base::Unretained(this)), + kDefaultBackoffPolicy, chrome_build_proto_, kUserSerialNumber, android_id, @@ -91,6 +128,7 @@ void CheckinRequestTest::CreateRequest(uint64 android_id, // Setting android_id_ and security_token_ to blank value, not used elsewhere // in the tests. + callback_called_ = false; android_id_ = kBlankAndroidId; security_token_ = kBlankSecurityToken; } @@ -98,14 +136,16 @@ void CheckinRequestTest::CreateRequest(uint64 android_id, void CheckinRequestTest::SetResponseStatusAndString( net::HttpStatusCode status_code, const std::string& response_data) { - net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); + net::TestURLFetcher* fetcher = + url_fetcher_factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); fetcher->set_response_code(status_code); fetcher->SetResponseString(response_data); } void CheckinRequestTest::CompleteFetch() { - net::TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); + net::TestURLFetcher* fetcher = + url_fetcher_factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); fetcher->delegate()->OnURLFetchComplete(fetcher); } @@ -165,8 +205,14 @@ TEST_F(CheckinRequestTest, ResponseBodyEmpty) { SetResponseStatusAndString(net::HTTP_OK, std::string()); CompleteFetch(); - EXPECT_EQ(0u, android_id_); - EXPECT_EQ(0u, security_token_); + EXPECT_FALSE(callback_called_); + + SetResponse(VALID_RESPONSE); + CompleteFetch(); + + EXPECT_TRUE(callback_called_); + EXPECT_EQ(kAndroidId, android_id_); + EXPECT_EQ(kSecurityToken, security_token_); } TEST_F(CheckinRequestTest, ResponseBodyCorrupted) { @@ -176,21 +222,57 @@ TEST_F(CheckinRequestTest, ResponseBodyCorrupted) { SetResponseStatusAndString(net::HTTP_OK, "Corrupted response body"); CompleteFetch(); + EXPECT_FALSE(callback_called_); + + SetResponse(VALID_RESPONSE); + CompleteFetch(); + + EXPECT_TRUE(callback_called_); + EXPECT_EQ(kAndroidId, android_id_); + EXPECT_EQ(kSecurityToken, security_token_); +} + +TEST_F(CheckinRequestTest, ResponseHttpStatusUnauthorized) { + CreateRequest(0u, 0u); + request_->Start(); + + SetResponseStatusAndString(net::HTTP_UNAUTHORIZED, std::string()); + CompleteFetch(); + + EXPECT_TRUE(callback_called_); EXPECT_EQ(0u, android_id_); EXPECT_EQ(0u, security_token_); } -TEST_F(CheckinRequestTest, ResponseHttpStatusNotOK) { +TEST_F(CheckinRequestTest, ResponseHttpStatusBadRequest) { CreateRequest(0u, 0u); request_->Start(); - SetResponseStatusAndString(net::HTTP_UNAUTHORIZED, std::string()); + SetResponseStatusAndString(net::HTTP_BAD_REQUEST, std::string()); CompleteFetch(); + EXPECT_TRUE(callback_called_); EXPECT_EQ(0u, android_id_); EXPECT_EQ(0u, security_token_); } +TEST_F(CheckinRequestTest, ResponseHttpStatusNotOK) { + CreateRequest(0u, 0u); + request_->Start(); + + SetResponseStatusAndString(net::HTTP_INTERNAL_SERVER_ERROR, std::string()); + CompleteFetch(); + + EXPECT_FALSE(callback_called_); + + SetResponse(VALID_RESPONSE); + CompleteFetch(); + + EXPECT_TRUE(callback_called_); + EXPECT_EQ(kAndroidId, android_id_); + EXPECT_EQ(kSecurityToken, security_token_); +} + TEST_F(CheckinRequestTest, ResponseMissingAndroidId) { CreateRequest(0u, 0u); request_->Start(); @@ -198,8 +280,14 @@ TEST_F(CheckinRequestTest, ResponseMissingAndroidId) { SetResponse(MISSING_ANDROID_ID); CompleteFetch(); - EXPECT_EQ(0u, android_id_); - EXPECT_EQ(0u, security_token_); + EXPECT_FALSE(callback_called_); + + SetResponse(VALID_RESPONSE); + CompleteFetch(); + + EXPECT_TRUE(callback_called_); + EXPECT_EQ(kAndroidId, android_id_); + EXPECT_EQ(kSecurityToken, security_token_); } TEST_F(CheckinRequestTest, ResponseMissingSecurityToken) { @@ -209,8 +297,14 @@ TEST_F(CheckinRequestTest, ResponseMissingSecurityToken) { SetResponse(MISSING_SECURITY_TOKEN); CompleteFetch(); - EXPECT_EQ(0u, android_id_); - EXPECT_EQ(0u, security_token_); + EXPECT_FALSE(callback_called_); + + SetResponse(VALID_RESPONSE); + CompleteFetch(); + + EXPECT_TRUE(callback_called_); + EXPECT_EQ(kAndroidId, android_id_); + EXPECT_EQ(kSecurityToken, security_token_); } TEST_F(CheckinRequestTest, AndroidIdEqualsZeroInResponse) { @@ -220,8 +314,14 @@ TEST_F(CheckinRequestTest, AndroidIdEqualsZeroInResponse) { SetResponse(ANDROID_ID_IS_ZER0); CompleteFetch(); - EXPECT_EQ(0u, android_id_); - EXPECT_EQ(0u, security_token_); + EXPECT_FALSE(callback_called_); + + SetResponse(VALID_RESPONSE); + CompleteFetch(); + + EXPECT_TRUE(callback_called_); + EXPECT_EQ(kAndroidId, android_id_); + EXPECT_EQ(kSecurityToken, security_token_); } TEST_F(CheckinRequestTest, SecurityTokenEqualsZeroInResponse) { @@ -231,8 +331,14 @@ TEST_F(CheckinRequestTest, SecurityTokenEqualsZeroInResponse) { SetResponse(SECURITY_TOKEN_IS_ZERO); CompleteFetch(); - EXPECT_EQ(0u, android_id_); - EXPECT_EQ(0u, security_token_); + EXPECT_FALSE(callback_called_); + + SetResponse(VALID_RESPONSE); + CompleteFetch(); + + EXPECT_TRUE(callback_called_); + EXPECT_EQ(kAndroidId, android_id_); + EXPECT_EQ(kSecurityToken, security_token_); } TEST_F(CheckinRequestTest, SuccessfulFirstTimeCheckin) { @@ -253,6 +359,7 @@ TEST_F(CheckinRequestTest, SuccessfulSubsequentCheckin) { SetResponse(VALID_RESPONSE); CompleteFetch(); + EXPECT_TRUE(callback_called_); EXPECT_EQ(kAndroidId, android_id_); EXPECT_EQ(kSecurityToken, security_token_); } diff --git a/google_apis/gcm/gcm_client_impl.cc b/google_apis/gcm/gcm_client_impl.cc index bf78cf6..abadb62 100644 --- a/google_apis/gcm/gcm_client_impl.cc +++ b/google_apis/gcm/gcm_client_impl.cc @@ -256,6 +256,7 @@ void GCMClientImpl::StartCheckin(int64 user_serial_number, base::Bind(&GCMClientImpl::OnCheckinCompleted, weak_ptr_factory_.GetWeakPtr(), user_serial_number), + kDefaultBackoffPolicy, chrome_build_proto_, user_serial_number, checkin_info.android_id, @@ -300,7 +301,8 @@ void GCMClientImpl::OnCheckinCompleted(int64 user_serial_number, void GCMClientImpl::OnDeviceCheckinCompleted(const CheckinInfo& checkin_info) { if (!checkin_info.IsValid()) { - // TODO(fgorski): Trigger a retry logic here. (no need to start over). + // TODO(fgorski): I don't think a retry here will help, we should probalby + // start over. By checking in with (0, 0). return; } diff --git a/google_apis/gcm/tools/mcs_probe.cc b/google_apis/gcm/tools/mcs_probe.cc index 7d05a29..b0de673 100644 --- a/google_apis/gcm/tools/mcs_probe.cc +++ b/google_apis/gcm/tools/mcs_probe.cc @@ -428,6 +428,7 @@ void MCSProbe::CheckIn() { chrome_build_proto.set_chrome_version(kChromeVersion); checkin_request_.reset(new CheckinRequest( base::Bind(&MCSProbe::OnCheckInCompleted, base::Unretained(this)), + kDefaultBackoffPolicy, chrome_build_proto, kUserSerialNumber, 0, |