Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / google_apis / gcm / engine / unregistration_request.cc
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.
4
5 #include "google_apis/gcm/engine/unregistration_request.h"
6
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"
20
21 namespace gcm {
22
23 namespace {
24
25 const char kRequestContentType[] = "application/x-www-form-urlencoded";
26
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";
37
38 // Response constants.
39 const char kDeletedPrefix[] = "deleted=";
40 const char kErrorPrefix[] = "Error=";
41 const char kInvalidParameters[] = "INVALID_PARAMETERS";
42
43
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));
50 }
51
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;
58   }
59
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;
69   }
70
71   std::string response;
72   if (!source->GetResponseAsString(&response)) {
73     DVLOG(1) << "Failed to get response body.";
74     return UnregistrationRequest::NO_RESPONSE_BODY;
75   }
76
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;
84   }
85
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;
92   }
93
94   DVLOG(1) << "Not able to parse a meaningful output from response body."
95            << response;
96   return UnregistrationRequest::RESPONSE_PARSING_FAILED;
97 }
98
99 }  // namespace
100
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) {
108 }
109
110 UnregistrationRequest::RequestInfo::~RequestInfo() {}
111
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) {
126 }
127
128 UnregistrationRequest::~UnregistrationRequest() {}
129
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());
135
136   url_fetcher_.reset(net::URLFetcher::Create(
137       registration_url_, net::URLFetcher::POST, this));
138   url_fetcher_->SetRequestContext(request_context_getter_);
139
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());
148
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);
156
157   DVLOG(1) << "Unregistration request: " << body;
158   url_fetcher_->SetUploadData(kRequestContentType, body);
159
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();
164 }
165
166 void UnregistrationRequest::RetryWithBackoff(bool update_backoff) {
167   if (update_backoff) {
168     url_fetcher_.reset();
169     backoff_entry_.InformOfRequest(false);
170   }
171
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;
187   }
188
189   Start();
190 }
191
192 void UnregistrationRequest::OnURLFetchComplete(const net::URLFetcher* source) {
193   UnregistrationRequest::Status status =
194       ParseFetcherResponse(source, request_info_.app_id);
195
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);
201
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;
209   }
210
211   // status == SUCCESS || HTTP_NOT_OK || NO_RESPONSE_BODY ||
212   // INVALID_PARAMETERS || UNKNOWN_ERROR
213
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_);
219   }
220
221   callback_.Run(status);
222 }
223
224 }  // namespace gcm