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"
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"
28 const char kRegistrationRequestContentType
[] =
29 "application/x-www-form-urlencoded";
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
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
;
72 RegistrationRequest::RequestInfo::RequestInfo(
74 uint64 security_token
,
75 const std::string
& app_id
)
76 : android_id(android_id
),
77 security_token(security_token
),
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
,
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
),
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());
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
);
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
;
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
),
155 DCHECK(custom_request_handler_
.get());
156 custom_request_handler_
->BuildRequestBody(body
);
159 void RegistrationRequest::RetryWithBackoff() {
160 DCHECK_GT(retries_left_
, 0);
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()
169 recorder_
->RecordRegistrationRetryDelayed(
170 request_info_
.app_id
,
172 backoff_entry_
.GetTimeUntilRelease().InMilliseconds(),
174 DCHECK(!weak_ptr_factory_
.HasWeakPtrs());
175 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
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);
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
213 if (source
->GetResponseCode() != net::HTTP_OK
) {
214 DLOG(ERROR
) << "URL fetching HTTP response code is not OK. It is "
215 << source
->GetResponseCode();
219 return UNKNOWN_ERROR
;
222 void RegistrationRequest::OnURLFetchComplete(const net::URLFetcher
* source
) {
224 Status status
= ParseResponse(source
, &token
);
225 recorder_
->RecordRegistrationResponse(
226 request_info_
.app_id
,
230 DCHECK(custom_request_handler_
.get());
231 custom_request_handler_
->ReportUMAs(
233 backoff_entry_
.failure_count(),
234 base::TimeTicks::Now() - request_start_time_
);
236 if (ShouldRetryWithStatus(status
)) {
237 if (retries_left_
> 0) {
242 status
= REACHED_MAX_RETRIES
;
243 recorder_
->RecordRegistrationResponse(
244 request_info_
.app_id
,
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
);