Roll src/third_party/WebKit 3529d49:06e8485 (svn 202554:202555)
[chromium-blink-merge.git] / google_apis / gcm / engine / registration_request.cc
blobfa66a7777dc93364a582feb70c2c89c5e4e316b0
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/registration_request.h"
7 #include "base/bind.h"
8 #include "base/location.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/values.h"
14 #include "google_apis/gcm/base/gcm_util.h"
15 #include "google_apis/gcm/monitoring/gcm_stats_recorder.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"
22 #include "url/gurl.h"
24 namespace gcm {
26 namespace {
28 const char kRegistrationRequestContentType[] =
29 "application/x-www-form-urlencoded";
31 // Request constants.
32 const char kAppIdKey[] = "app";
33 const char kDeviceIdKey[] = "device";
34 const char kLoginHeader[] = "AidLogin";
36 // Response constants.
37 const char kErrorPrefix[] = "Error=";
38 const char kTokenPrefix[] = "token=";
39 const char kDeviceRegistrationError[] = "PHONE_REGISTRATION_ERROR";
40 const char kAuthenticationFailed[] = "AUTHENTICATION_FAILED";
41 const char kInvalidSender[] = "INVALID_SENDER";
42 const char kInvalidParameters[] = "INVALID_PARAMETERS";
44 // Gets correct status from the error message.
45 RegistrationRequest::Status GetStatusFromError(const std::string& error) {
46 // TODO(fgorski): Improve error parsing in case there is nore then just an
47 // Error=ERROR_STRING in response.
48 if (error.find(kDeviceRegistrationError) != std::string::npos)
49 return RegistrationRequest::DEVICE_REGISTRATION_ERROR;
50 if (error.find(kAuthenticationFailed) != std::string::npos)
51 return RegistrationRequest::AUTHENTICATION_FAILED;
52 if (error.find(kInvalidSender) != std::string::npos)
53 return RegistrationRequest::INVALID_SENDER;
54 if (error.find(kInvalidParameters) != std::string::npos)
55 return RegistrationRequest::INVALID_PARAMETERS;
56 return RegistrationRequest::UNKNOWN_ERROR;
59 // Indicates whether a retry attempt should be made based on the status of the
60 // last request.
61 bool ShouldRetryWithStatus(RegistrationRequest::Status status) {
62 return status == RegistrationRequest::UNKNOWN_ERROR ||
63 status == RegistrationRequest::AUTHENTICATION_FAILED ||
64 status == RegistrationRequest::DEVICE_REGISTRATION_ERROR ||
65 status == RegistrationRequest::HTTP_NOT_OK ||
66 status == RegistrationRequest::URL_FETCHING_FAILED ||
67 status == RegistrationRequest::RESPONSE_PARSING_FAILED;
70 } // namespace
72 RegistrationRequest::RequestInfo::RequestInfo(
73 uint64 android_id,
74 uint64 security_token,
75 const std::string& app_id)
76 : android_id(android_id),
77 security_token(security_token),
78 app_id(app_id) {
79 DCHECK(android_id != 0UL);
80 DCHECK(security_token != 0UL);
83 RegistrationRequest::RequestInfo::~RequestInfo() {}
85 RegistrationRequest::CustomRequestHandler::CustomRequestHandler() {}
87 RegistrationRequest::CustomRequestHandler::~CustomRequestHandler() {}
89 RegistrationRequest::RegistrationRequest(
90 const GURL& registration_url,
91 const RequestInfo& request_info,
92 scoped_ptr<CustomRequestHandler> custom_request_handler,
93 const net::BackoffEntry::Policy& backoff_policy,
94 const RegistrationCallback& callback,
95 int max_retry_count,
96 scoped_refptr<net::URLRequestContextGetter> request_context_getter,
97 GCMStatsRecorder* recorder,
98 const std::string& source_to_record)
99 : callback_(callback),
100 request_info_(request_info),
101 custom_request_handler_(custom_request_handler.Pass()),
102 registration_url_(registration_url),
103 backoff_entry_(&backoff_policy),
104 request_context_getter_(request_context_getter),
105 retries_left_(max_retry_count),
106 recorder_(recorder),
107 source_to_record_(source_to_record),
108 weak_ptr_factory_(this) {
109 DCHECK_GE(max_retry_count, 0);
112 RegistrationRequest::~RegistrationRequest() {}
114 void RegistrationRequest::Start() {
115 DCHECK(!callback_.is_null());
116 DCHECK(!url_fetcher_.get());
118 url_fetcher_ =
119 net::URLFetcher::Create(registration_url_, net::URLFetcher::POST, this);
120 url_fetcher_->SetRequestContext(request_context_getter_.get());
121 url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
122 net::LOAD_DO_NOT_SAVE_COOKIES);
124 std::string extra_headers;
125 BuildRequestHeaders(&extra_headers);
126 url_fetcher_->SetExtraRequestHeaders(extra_headers);
128 std::string body;
129 BuildRequestBody(&body);
131 DVLOG(1) << "Performing registration for: " << request_info_.app_id;
132 DVLOG(1) << "Registration request: " << body;
133 url_fetcher_->SetUploadData(kRegistrationRequestContentType, body);
134 recorder_->RecordRegistrationSent(request_info_.app_id, source_to_record_);
135 request_start_time_ = base::TimeTicks::Now();
136 url_fetcher_->Start();
139 void RegistrationRequest::BuildRequestHeaders(std::string* extra_headers) {
140 net::HttpRequestHeaders headers;
141 headers.SetHeader(
142 net::HttpRequestHeaders::kAuthorization,
143 std::string(kLoginHeader) + " " +
144 base::Uint64ToString(request_info_.android_id) + ":" +
145 base::Uint64ToString(request_info_.security_token));
146 *extra_headers = headers.ToString();
149 void RegistrationRequest::BuildRequestBody(std::string* body) {
150 BuildFormEncoding(kAppIdKey, request_info_.app_id, body);
151 BuildFormEncoding(kDeviceIdKey,
152 base::Uint64ToString(request_info_.android_id),
153 body);
155 DCHECK(custom_request_handler_.get());
156 custom_request_handler_->BuildRequestBody(body);
159 void RegistrationRequest::RetryWithBackoff() {
160 DCHECK_GT(retries_left_, 0);
161 --retries_left_;
162 url_fetcher_.reset();
163 backoff_entry_.InformOfRequest(false);
165 DVLOG(1) << "Delaying GCM registration of app: "
166 << request_info_.app_id << ", for "
167 << backoff_entry_.GetTimeUntilRelease().InMilliseconds()
168 << " milliseconds.";
169 recorder_->RecordRegistrationRetryDelayed(
170 request_info_.app_id,
171 source_to_record_,
172 backoff_entry_.GetTimeUntilRelease().InMilliseconds(),
173 retries_left_ + 1);
174 DCHECK(!weak_ptr_factory_.HasWeakPtrs());
175 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
176 FROM_HERE,
177 base::Bind(&RegistrationRequest::Start, weak_ptr_factory_.GetWeakPtr()),
178 backoff_entry_.GetTimeUntilRelease());
181 RegistrationRequest::Status RegistrationRequest::ParseResponse(
182 const net::URLFetcher* source, std::string* token) {
183 if (!source->GetStatus().is_success()) {
184 LOG(ERROR) << "URL fetching failed.";
185 return URL_FETCHING_FAILED;
188 std::string response;
189 if (!source->GetResponseAsString(&response)) {
190 LOG(ERROR) << "Failed to parse registration response as a string.";
191 return RESPONSE_PARSING_FAILED;
194 if (source->GetResponseCode() == net::HTTP_OK) {
195 size_t token_pos = response.find(kTokenPrefix);
196 if (token_pos != std::string::npos) {
197 *token = response.substr(token_pos + arraysize(kTokenPrefix) - 1);
198 return SUCCESS;
202 // If we are able to parse a meaningful known error, let's do so. Some errors
203 // will have HTTP_BAD_REQUEST, some will have HTTP_OK response code.
204 size_t error_pos = response.find(kErrorPrefix);
205 if (error_pos != std::string::npos) {
206 std::string error = response.substr(
207 error_pos + arraysize(kErrorPrefix) - 1);
208 return GetStatusFromError(error);
211 // If we cannot tell what the error is, but at least we know response code was
212 // not OK.
213 if (source->GetResponseCode() != net::HTTP_OK) {
214 DLOG(ERROR) << "URL fetching HTTP response code is not OK. It is "
215 << source->GetResponseCode();
216 return HTTP_NOT_OK;
219 return UNKNOWN_ERROR;
222 void RegistrationRequest::OnURLFetchComplete(const net::URLFetcher* source) {
223 std::string token;
224 Status status = ParseResponse(source, &token);
225 recorder_->RecordRegistrationResponse(
226 request_info_.app_id,
227 source_to_record_,
228 status);
230 DCHECK(custom_request_handler_.get());
231 custom_request_handler_->ReportUMAs(
232 status,
233 backoff_entry_.failure_count(),
234 base::TimeTicks::Now() - request_start_time_);
236 if (ShouldRetryWithStatus(status)) {
237 if (retries_left_ > 0) {
238 RetryWithBackoff();
239 return;
242 status = REACHED_MAX_RETRIES;
243 recorder_->RecordRegistrationResponse(
244 request_info_.app_id,
245 source_to_record_,
246 status);
248 // Only REACHED_MAX_RETRIES is reported because the function will skip
249 // reporting count and time when status is not SUCCESS.
250 DCHECK(custom_request_handler_.get());
251 custom_request_handler_->ReportUMAs(status, 0, base::TimeDelta());
254 callback_.Run(status, token);
257 } // namespace gcm