Roll src/third_party/WebKit 3529d49:06e8485 (svn 202554:202555)
[chromium-blink-merge.git] / google_apis / gcm / engine / unregistration_request.cc
blob6cde58190459f8a6bf50265a9fd12152c0d88928
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/location.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_piece.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/values.h"
13 #include "google_apis/gcm/base/gcm_util.h"
14 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
15 #include "net/base/escape.h"
16 #include "net/base/load_flags.h"
17 #include "net/http/http_request_headers.h"
18 #include "net/http/http_status_code.h"
19 #include "net/url_request/url_fetcher.h"
20 #include "net/url_request/url_request_context_getter.h"
21 #include "net/url_request/url_request_status.h"
23 namespace gcm {
25 namespace {
27 const char kRequestContentType[] = "application/x-www-form-urlencoded";
29 // Request constants.
30 const char kAppIdKey[] = "app";
31 const char kDeleteKey[] = "delete";
32 const char kDeleteValue[] = "true";
33 const char kDeviceIdKey[] = "device";
34 const char kLoginHeader[] = "AidLogin";
36 } // namespace
38 UnregistrationRequest::RequestInfo::RequestInfo(
39 uint64 android_id,
40 uint64 security_token,
41 const std::string& app_id)
42 : android_id(android_id),
43 security_token(security_token),
44 app_id(app_id) {
45 DCHECK(android_id != 0UL);
46 DCHECK(security_token != 0UL);
49 UnregistrationRequest::RequestInfo::~RequestInfo() {}
51 UnregistrationRequest::CustomRequestHandler::CustomRequestHandler() {}
53 UnregistrationRequest::CustomRequestHandler::~CustomRequestHandler() {}
55 UnregistrationRequest::UnregistrationRequest(
56 const GURL& registration_url,
57 const RequestInfo& request_info,
58 scoped_ptr<CustomRequestHandler> custom_request_handler,
59 const net::BackoffEntry::Policy& backoff_policy,
60 const UnregistrationCallback& callback,
61 int max_retry_count,
62 scoped_refptr<net::URLRequestContextGetter> request_context_getter,
63 GCMStatsRecorder* recorder,
64 const std::string& source_to_record)
65 : callback_(callback),
66 request_info_(request_info),
67 custom_request_handler_(custom_request_handler.Pass()),
68 registration_url_(registration_url),
69 backoff_entry_(&backoff_policy),
70 request_context_getter_(request_context_getter),
71 retries_left_(max_retry_count),
72 recorder_(recorder),
73 source_to_record_(source_to_record),
74 weak_ptr_factory_(this) {
75 DCHECK_GE(max_retry_count, 0);
78 UnregistrationRequest::~UnregistrationRequest() {}
80 void UnregistrationRequest::Start() {
81 DCHECK(!callback_.is_null());
82 DCHECK(!url_fetcher_.get());
84 url_fetcher_ =
85 net::URLFetcher::Create(registration_url_, net::URLFetcher::POST, this);
86 url_fetcher_->SetRequestContext(request_context_getter_.get());
87 url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
88 net::LOAD_DO_NOT_SAVE_COOKIES);
90 std::string extra_headers;
91 BuildRequestHeaders(&extra_headers);
92 url_fetcher_->SetExtraRequestHeaders(extra_headers);
94 std::string body;
95 BuildRequestBody(&body);
97 DVLOG(1) << "Unregistration request: " << body;
98 url_fetcher_->SetUploadData(kRequestContentType, body);
100 DVLOG(1) << "Performing unregistration for: " << request_info_.app_id;
101 recorder_->RecordUnregistrationSent(request_info_.app_id, source_to_record_);
102 request_start_time_ = base::TimeTicks::Now();
103 url_fetcher_->Start();
106 void UnregistrationRequest::BuildRequestHeaders(std::string* extra_headers) {
107 net::HttpRequestHeaders headers;
108 headers.SetHeader(
109 net::HttpRequestHeaders::kAuthorization,
110 std::string(kLoginHeader) + " " +
111 base::Uint64ToString(request_info_.android_id) + ":" +
112 base::Uint64ToString(request_info_.security_token));
113 headers.SetHeader(kAppIdKey, request_info_.app_id);
114 *extra_headers = headers.ToString();
117 void UnregistrationRequest::BuildRequestBody(std::string* body) {
118 BuildFormEncoding(kAppIdKey, request_info_.app_id, body);
119 BuildFormEncoding(kDeviceIdKey,
120 base::Uint64ToString(request_info_.android_id),
121 body);
122 BuildFormEncoding(kDeleteKey, kDeleteValue, body);
124 DCHECK(custom_request_handler_.get());
125 custom_request_handler_->BuildRequestBody(body);
128 UnregistrationRequest::Status UnregistrationRequest::ParseResponse(
129 const net::URLFetcher* source) {
130 if (!source->GetStatus().is_success()) {
131 DVLOG(1) << "Fetcher failed";
132 return URL_FETCHING_FAILED;
135 net::HttpStatusCode response_status = static_cast<net::HttpStatusCode>(
136 source->GetResponseCode());
137 if (response_status != net::HTTP_OK) {
138 DVLOG(1) << "HTTP Status code is not OK, but: " << response_status;
139 if (response_status == net::HTTP_SERVICE_UNAVAILABLE)
140 return SERVICE_UNAVAILABLE;
141 if (response_status == net::HTTP_INTERNAL_SERVER_ERROR)
142 return INTERNAL_SERVER_ERROR;
143 return HTTP_NOT_OK;
146 DCHECK(custom_request_handler_.get());
147 return custom_request_handler_->ParseResponse(source);
150 void UnregistrationRequest::RetryWithBackoff() {
151 DCHECK_GT(retries_left_, 0);
152 --retries_left_;
153 url_fetcher_.reset();
154 backoff_entry_.InformOfRequest(false);
156 DVLOG(1) << "Delaying GCM unregistration of app: "
157 << request_info_.app_id << ", for "
158 << backoff_entry_.GetTimeUntilRelease().InMilliseconds()
159 << " milliseconds.";
160 recorder_->RecordUnregistrationRetryDelayed(
161 request_info_.app_id,
162 source_to_record_,
163 backoff_entry_.GetTimeUntilRelease().InMilliseconds(),
164 retries_left_ + 1);
165 DCHECK(!weak_ptr_factory_.HasWeakPtrs());
166 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
167 FROM_HERE,
168 base::Bind(&UnregistrationRequest::Start, weak_ptr_factory_.GetWeakPtr()),
169 backoff_entry_.GetTimeUntilRelease());
172 void UnregistrationRequest::OnURLFetchComplete(const net::URLFetcher* source) {
173 UnregistrationRequest::Status status = ParseResponse(source);
175 DVLOG(1) << "UnregistrationRequestStauts: " << status;
177 DCHECK(custom_request_handler_.get());
178 custom_request_handler_->ReportUMAs(
179 status,
180 backoff_entry_.failure_count(),
181 base::TimeTicks::Now() - request_start_time_);
183 recorder_->RecordUnregistrationResponse(
184 request_info_.app_id, source_to_record_, status);
186 if (status == URL_FETCHING_FAILED ||
187 status == HTTP_NOT_OK ||
188 status == NO_RESPONSE_BODY ||
189 status == SERVICE_UNAVAILABLE ||
190 status == INTERNAL_SERVER_ERROR ||
191 status == INCORRECT_APP_ID ||
192 status == RESPONSE_PARSING_FAILED) {
193 if (retries_left_ > 0) {
194 RetryWithBackoff();
195 return;
198 status = REACHED_MAX_RETRIES;
199 recorder_->RecordUnregistrationResponse(
200 request_info_.app_id, source_to_record_, status);
202 // Only REACHED_MAX_RETRIES is reported because the function will skip
203 // reporting count and time when status is not SUCCESS.
204 DCHECK(custom_request_handler_.get());
205 custom_request_handler_->ReportUMAs(status, 0, base::TimeDelta());
208 // status == SUCCESS || INVALID_PARAMETERS || UNKNOWN_ERROR ||
209 // REACHED_MAX_RETRIES
211 callback_.Run(status);
214 } // namespace gcm