[GCM] Add more UMA to GCM
[chromium-blink-merge.git] / google_apis / gcm / engine / unregistration_request.cc
blob2b8c97dadf1066f56a64d31fd657bbda929059c1
1 // Copyright 2014 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.
5 #include "google_apis/gcm/engine/unregistration_request.h"
7 #include "base/bind.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_piece.h"
12 #include "base/values.h"
13 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
14 #include "net/base/escape.h"
15 #include "net/http/http_request_headers.h"
16 #include "net/http/http_status_code.h"
17 #include "net/url_request/url_fetcher.h"
18 #include "net/url_request/url_request_context_getter.h"
19 #include "net/url_request/url_request_status.h"
21 namespace gcm {
23 namespace {
25 const char kRequestContentType[] = "application/x-www-form-urlencoded";
27 // Request constants.
28 const char kAppIdKey[] = "app";
29 const char kDeleteKey[] = "delete";
30 const char kDeleteValue[] = "true";
31 const char kDeviceIdKey[] = "device";
32 const char kLoginHeader[] = "AidLogin";
33 const char kUnregistrationCallerKey[] = "gcm_unreg_caller";
34 // We are going to set the value to "false" in order to forcefully unregister
35 // the application.
36 const char kUnregistrationCallerValue[] = "false";
38 // Response constants.
39 const char kDeletedPrefix[] = "deleted=";
40 const char kErrorPrefix[] = "Error=";
41 const char kInvalidParameters[] = "INVALID_PARAMETERS";
44 void BuildFormEncoding(const std::string& key,
45 const std::string& value,
46 std::string* out) {
47 if (!out->empty())
48 out->append("&");
49 out->append(key + "=" + net::EscapeUrlEncodedData(value, true));
52 UnregistrationRequest::Status ParseFetcherResponse(
53 const net::URLFetcher* source,
54 std::string request_app_id) {
55 if (!source->GetStatus().is_success()) {
56 DVLOG(1) << "Fetcher failed";
57 return UnregistrationRequest::URL_FETCHING_FAILED;
60 net::HttpStatusCode response_status = static_cast<net::HttpStatusCode>(
61 source->GetResponseCode());
62 if (response_status != net::HTTP_OK) {
63 DVLOG(1) << "HTTP Status code is not OK, but: " << response_status;
64 if (response_status == net::HTTP_SERVICE_UNAVAILABLE)
65 return UnregistrationRequest::SERVICE_UNAVAILABLE;
66 else if (response_status == net::HTTP_INTERNAL_SERVER_ERROR)
67 return UnregistrationRequest::INTERNAL_SERVER_ERROR;
68 return UnregistrationRequest::HTTP_NOT_OK;
71 std::string response;
72 if (!source->GetResponseAsString(&response)) {
73 DVLOG(1) << "Failed to get response body.";
74 return UnregistrationRequest::NO_RESPONSE_BODY;
77 DVLOG(1) << "Parsing unregistration response.";
78 if (response.find(kDeletedPrefix) != std::string::npos) {
79 std::string app_id = response.substr(
80 response.find(kDeletedPrefix) + arraysize(kDeletedPrefix) - 1);
81 if (app_id == request_app_id)
82 return UnregistrationRequest::SUCCESS;
83 return UnregistrationRequest::INCORRECT_APP_ID;
86 if (response.find(kErrorPrefix) != std::string::npos) {
87 std::string error = response.substr(
88 response.find(kErrorPrefix) + arraysize(kErrorPrefix) - 1);
89 if (error == kInvalidParameters)
90 return UnregistrationRequest::INVALID_PARAMETERS;
91 return UnregistrationRequest::UNKNOWN_ERROR;
94 DVLOG(1) << "Not able to parse a meaningful output from response body."
95 << response;
96 return UnregistrationRequest::RESPONSE_PARSING_FAILED;
99 } // namespace
101 UnregistrationRequest::RequestInfo::RequestInfo(
102 uint64 android_id,
103 uint64 security_token,
104 const std::string& app_id)
105 : android_id(android_id),
106 security_token(security_token),
107 app_id(app_id) {
110 UnregistrationRequest::RequestInfo::~RequestInfo() {}
112 UnregistrationRequest::UnregistrationRequest(
113 const GURL& registration_url,
114 const RequestInfo& request_info,
115 const net::BackoffEntry::Policy& backoff_policy,
116 const UnregistrationCallback& callback,
117 scoped_refptr<net::URLRequestContextGetter> request_context_getter,
118 GCMStatsRecorder* recorder)
119 : callback_(callback),
120 request_info_(request_info),
121 registration_url_(registration_url),
122 backoff_entry_(&backoff_policy),
123 request_context_getter_(request_context_getter),
124 recorder_(recorder),
125 weak_ptr_factory_(this) {
128 UnregistrationRequest::~UnregistrationRequest() {}
130 void UnregistrationRequest::Start() {
131 DCHECK(!callback_.is_null());
132 DCHECK(request_info_.android_id != 0UL);
133 DCHECK(request_info_.security_token != 0UL);
134 DCHECK(!url_fetcher_.get());
136 url_fetcher_.reset(net::URLFetcher::Create(
137 registration_url_, net::URLFetcher::POST, this));
138 url_fetcher_->SetRequestContext(request_context_getter_);
140 std::string android_id = base::Uint64ToString(request_info_.android_id);
141 std::string auth_header =
142 std::string(kLoginHeader) + " " + android_id + ":" +
143 base::Uint64ToString(request_info_.security_token);
144 net::HttpRequestHeaders headers;
145 headers.SetHeader(net::HttpRequestHeaders::kAuthorization, auth_header);
146 headers.SetHeader(kAppIdKey, request_info_.app_id);
147 url_fetcher_->SetExtraRequestHeaders(headers.ToString());
149 std::string body;
150 BuildFormEncoding(kAppIdKey, request_info_.app_id, &body);
151 BuildFormEncoding(kDeviceIdKey, android_id, &body);
152 BuildFormEncoding(kDeleteKey, kDeleteValue, &body);
153 BuildFormEncoding(kUnregistrationCallerKey,
154 kUnregistrationCallerValue,
155 &body);
157 DVLOG(1) << "Unregistration request: " << body;
158 url_fetcher_->SetUploadData(kRequestContentType, body);
160 DVLOG(1) << "Performing unregistration for: " << request_info_.app_id;
161 recorder_->RecordUnregistrationSent(request_info_.app_id);
162 request_start_time_ = base::TimeTicks::Now();
163 url_fetcher_->Start();
166 void UnregistrationRequest::RetryWithBackoff(bool update_backoff) {
167 if (update_backoff) {
168 url_fetcher_.reset();
169 backoff_entry_.InformOfRequest(false);
172 if (backoff_entry_.ShouldRejectRequest()) {
173 DVLOG(1) << "Delaying GCM unregistration of app: "
174 << request_info_.app_id << ", for "
175 << backoff_entry_.GetTimeUntilRelease().InMilliseconds()
176 << " milliseconds.";
177 recorder_->RecordUnregistrationRetryDelayed(
178 request_info_.app_id,
179 backoff_entry_.GetTimeUntilRelease().InMilliseconds());
180 base::MessageLoop::current()->PostDelayedTask(
181 FROM_HERE,
182 base::Bind(&UnregistrationRequest::RetryWithBackoff,
183 weak_ptr_factory_.GetWeakPtr(),
184 false),
185 backoff_entry_.GetTimeUntilRelease());
186 return;
189 Start();
192 void UnregistrationRequest::OnURLFetchComplete(const net::URLFetcher* source) {
193 UnregistrationRequest::Status status =
194 ParseFetcherResponse(source, request_info_.app_id);
196 DVLOG(1) << "UnregistrationRequestStauts: " << status;
197 UMA_HISTOGRAM_ENUMERATION("GCM.UnregistrationRequestStatus",
198 status,
199 UNREGISTRATION_STATUS_COUNT);
200 recorder_->RecordUnregistrationResponse(request_info_.app_id, status);
202 if (status == URL_FETCHING_FAILED ||
203 status == SERVICE_UNAVAILABLE ||
204 status == INTERNAL_SERVER_ERROR ||
205 status == INCORRECT_APP_ID ||
206 status == RESPONSE_PARSING_FAILED) {
207 RetryWithBackoff(true);
208 return;
211 // status == SUCCESS || HTTP_NOT_OK || NO_RESPONSE_BODY ||
212 // INVALID_PARAMETERS || UNKNOWN_ERROR
214 if (status == SUCCESS) {
215 UMA_HISTOGRAM_COUNTS("GCM.UnregistrationRetryCount",
216 backoff_entry_.failure_count());
217 UMA_HISTOGRAM_TIMES("GCM.UnregistrationCompleteTime",
218 base::TimeTicks::Now() - request_start_time_);
221 callback_.Run(status);
224 } // namespace gcm