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/registration_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/values.h" 12 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h" 13 #include "net/base/escape.h" 14 #include "net/http/http_request_headers.h" 15 #include "net/http/http_status_code.h" 16 #include "net/url_request/url_fetcher.h" 17 #include "net/url_request/url_request_context_getter.h" 18 #include "net/url_request/url_request_status.h" 19 #include "url/gurl.h" 20 21 namespace gcm { 22 23 namespace { 24 25 const char kRegistrationRequestContentType[] = 26 "application/x-www-form-urlencoded"; 27 28 // Request constants. 29 const char kAppIdKey[] = "app"; 30 const char kDeviceIdKey[] = "device"; 31 const char kLoginHeader[] = "AidLogin"; 32 const char kSenderKey[] = "sender"; 33 34 // Request validation constants. 35 const size_t kMaxSenders = 100; 36 37 // Response constants. 38 const char kErrorPrefix[] = "Error="; 39 const char kTokenPrefix[] = "token="; 40 const char kDeviceRegistrationError[] = "PHONE_REGISTRATION_ERROR"; 41 const char kAuthenticationFailed[] = "AUTHENTICATION_FAILED"; 42 const char kInvalidSender[] = "INVALID_SENDER"; 43 const char kInvalidParameters[] = "INVALID_PARAMETERS"; 44 45 void BuildFormEncoding(const std::string& key, 46 const std::string& value, 47 std::string* out) { 48 if (!out->empty()) 49 out->append("&"); 50 out->append(key + "=" + net::EscapeUrlEncodedData(value, true)); 51 } 52 53 // Gets correct status from the error message. 54 RegistrationRequest::Status GetStatusFromError(const std::string& error) { 55 // TODO(fgorski): Improve error parsing in case there is nore then just an 56 // Error=ERROR_STRING in response. 57 if (error.find(kDeviceRegistrationError) != std::string::npos) 58 return RegistrationRequest::DEVICE_REGISTRATION_ERROR; 59 if (error.find(kAuthenticationFailed) != std::string::npos) 60 return RegistrationRequest::AUTHENTICATION_FAILED; 61 if (error.find(kInvalidSender) != std::string::npos) 62 return RegistrationRequest::INVALID_SENDER; 63 if (error.find(kInvalidParameters) != std::string::npos) 64 return RegistrationRequest::INVALID_PARAMETERS; 65 return RegistrationRequest::UNKNOWN_ERROR; 66 } 67 68 // Indicates whether a retry attempt should be made based on the status of the 69 // last request. 70 bool ShouldRetryWithStatus(RegistrationRequest::Status status) { 71 return status == RegistrationRequest::UNKNOWN_ERROR || 72 status == RegistrationRequest::AUTHENTICATION_FAILED || 73 status == RegistrationRequest::DEVICE_REGISTRATION_ERROR || 74 status == RegistrationRequest::HTTP_NOT_OK || 75 status == RegistrationRequest::URL_FETCHING_FAILED || 76 status == RegistrationRequest::RESPONSE_PARSING_FAILED; 77 } 78 79 void RecordRegistrationStatusToUMA(RegistrationRequest::Status status) { 80 UMA_HISTOGRAM_ENUMERATION("GCM.RegistrationRequestStatus", status, 81 RegistrationRequest::STATUS_COUNT); 82 } 83 84 } // namespace 85 86 RegistrationRequest::RequestInfo::RequestInfo( 87 uint64 android_id, 88 uint64 security_token, 89 const std::string& app_id, 90 const std::vector<std::string>& sender_ids) 91 : android_id(android_id), 92 security_token(security_token), 93 app_id(app_id), 94 sender_ids(sender_ids) { 95 } 96 97 RegistrationRequest::RequestInfo::~RequestInfo() {} 98 99 RegistrationRequest::RegistrationRequest( 100 const GURL& registration_url, 101 const RequestInfo& request_info, 102 const net::BackoffEntry::Policy& backoff_policy, 103 const RegistrationCallback& callback, 104 int max_retry_count, 105 scoped_refptr<net::URLRequestContextGetter> request_context_getter, 106 GCMStatsRecorder* recorder) 107 : callback_(callback), 108 request_info_(request_info), 109 registration_url_(registration_url), 110 backoff_entry_(&backoff_policy), 111 request_context_getter_(request_context_getter), 112 retries_left_(max_retry_count), 113 recorder_(recorder), 114 weak_ptr_factory_(this) { 115 DCHECK_GE(max_retry_count, 0); 116 } 117 118 RegistrationRequest::~RegistrationRequest() {} 119 120 void RegistrationRequest::Start() { 121 DCHECK(!callback_.is_null()); 122 DCHECK(request_info_.android_id != 0UL); 123 DCHECK(request_info_.security_token != 0UL); 124 DCHECK(0 < request_info_.sender_ids.size() && 125 request_info_.sender_ids.size() <= kMaxSenders); 126 127 DCHECK(!url_fetcher_.get()); 128 url_fetcher_.reset(net::URLFetcher::Create( 129 registration_url_, net::URLFetcher::POST, this)); 130 url_fetcher_->SetRequestContext(request_context_getter_); 131 132 std::string android_id = base::Uint64ToString(request_info_.android_id); 133 std::string auth_header = 134 std::string(net::HttpRequestHeaders::kAuthorization) + ": " + 135 kLoginHeader + " " + android_id + ":" + 136 base::Uint64ToString(request_info_.security_token); 137 url_fetcher_->SetExtraRequestHeaders(auth_header); 138 139 std::string body; 140 BuildFormEncoding(kAppIdKey, request_info_.app_id, &body); 141 BuildFormEncoding(kDeviceIdKey, android_id, &body); 142 143 std::string senders; 144 for (std::vector<std::string>::const_iterator iter = 145 request_info_.sender_ids.begin(); 146 iter != request_info_.sender_ids.end(); 147 ++iter) { 148 DCHECK(!iter->empty()); 149 if (!senders.empty()) 150 senders.append(","); 151 senders.append(*iter); 152 } 153 BuildFormEncoding(kSenderKey, senders, &body); 154 UMA_HISTOGRAM_COUNTS("GCM.RegistrationSenderIdCount", 155 request_info_.sender_ids.size()); 156 157 DVLOG(1) << "Performing registration for: " << request_info_.app_id; 158 DVLOG(1) << "Registration request: " << body; 159 url_fetcher_->SetUploadData(kRegistrationRequestContentType, body); 160 recorder_->RecordRegistrationSent(request_info_.app_id, senders); 161 request_start_time_ = base::TimeTicks::Now(); 162 url_fetcher_->Start(); 163 } 164 165 void RegistrationRequest::RetryWithBackoff(bool update_backoff) { 166 if (update_backoff) { 167 DCHECK_GT(retries_left_, 0); 168 --retries_left_; 169 url_fetcher_.reset(); 170 backoff_entry_.InformOfRequest(false); 171 } 172 173 if (backoff_entry_.ShouldRejectRequest()) { 174 DVLOG(1) << "Delaying GCM registration of app: " 175 << request_info_.app_id << ", for " 176 << backoff_entry_.GetTimeUntilRelease().InMilliseconds() 177 << " milliseconds."; 178 base::MessageLoop::current()->PostDelayedTask( 179 FROM_HERE, 180 base::Bind(&RegistrationRequest::RetryWithBackoff, 181 weak_ptr_factory_.GetWeakPtr(), 182 false), 183 backoff_entry_.GetTimeUntilRelease()); 184 return; 185 } 186 187 Start(); 188 } 189 190 RegistrationRequest::Status RegistrationRequest::ParseResponse( 191 const net::URLFetcher* source, std::string* token) { 192 if (!source->GetStatus().is_success()) { 193 LOG(ERROR) << "URL fetching failed."; 194 return URL_FETCHING_FAILED; 195 } 196 197 std::string response; 198 if (!source->GetResponseAsString(&response)) { 199 LOG(ERROR) << "Failed to parse registration response as a string."; 200 return RESPONSE_PARSING_FAILED; 201 } 202 203 if (source->GetResponseCode() == net::HTTP_OK) { 204 size_t token_pos = response.find(kTokenPrefix); 205 if (token_pos != std::string::npos) { 206 *token = response.substr(token_pos + arraysize(kTokenPrefix) - 1); 207 return SUCCESS; 208 } 209 } 210 211 // If we are able to parse a meaningful known error, let's do so. Some errors 212 // will have HTTP_BAD_REQUEST, some will have HTTP_OK response code. 213 size_t error_pos = response.find(kErrorPrefix); 214 if (error_pos != std::string::npos) { 215 std::string error = response.substr( 216 error_pos + arraysize(kErrorPrefix) - 1); 217 return GetStatusFromError(error); 218 } 219 220 // If we cannot tell what the error is, but at least we know response code was 221 // not OK. 222 if (source->GetResponseCode() != net::HTTP_OK) { 223 DLOG(ERROR) << "URL fetching HTTP response code is not OK. It is " 224 << source->GetResponseCode(); 225 return HTTP_NOT_OK; 226 } 227 228 return UNKNOWN_ERROR; 229 } 230 231 void RegistrationRequest::OnURLFetchComplete(const net::URLFetcher* source) { 232 std::string token; 233 Status status = ParseResponse(source, &token); 234 RecordRegistrationStatusToUMA(status); 235 recorder_->RecordRegistrationResponse( 236 request_info_.app_id, 237 request_info_.sender_ids, 238 status); 239 240 if (ShouldRetryWithStatus(status)) { 241 if (retries_left_ > 0) { 242 recorder_->RecordRegistrationRetryRequested( 243 request_info_.app_id, 244 request_info_.sender_ids, 245 retries_left_); 246 RetryWithBackoff(true); 247 return; 248 } 249 250 status = REACHED_MAX_RETRIES; 251 recorder_->RecordRegistrationResponse( 252 request_info_.app_id, 253 request_info_.sender_ids, 254 status); 255 RecordRegistrationStatusToUMA(status); 256 } 257 258 if (status == SUCCESS) { 259 UMA_HISTOGRAM_COUNTS("GCM.RegistrationRetryCount", 260 backoff_entry_.failure_count()); 261 UMA_HISTOGRAM_TIMES("GCM.RegistrationCompleteTime", 262 base::TimeTicks::Now() - request_start_time_); 263 } 264 callback_.Run(status, token); 265 } 266 267 } // namespace gcm 268