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"
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"
27 const char kRequestContentType
[] = "application/x-www-form-urlencoded";
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";
38 UnregistrationRequest::RequestInfo::RequestInfo(
40 uint64 security_token
,
41 const std::string
& app_id
)
42 : android_id(android_id
),
43 security_token(security_token
),
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
,
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
),
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());
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
);
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
;
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
),
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
;
146 DCHECK(custom_request_handler_
.get());
147 return custom_request_handler_
->ParseResponse(source
);
150 void UnregistrationRequest::RetryWithBackoff() {
151 DCHECK_GT(retries_left_
, 0);
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()
160 recorder_
->RecordUnregistrationRetryDelayed(
161 request_info_
.app_id
,
163 backoff_entry_
.GetTimeUntilRelease().InMilliseconds(),
165 DCHECK(!weak_ptr_factory_
.HasWeakPtrs());
166 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
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(
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) {
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
);