Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(857)

Side by Side Diff: components/copresence/rpc/rpc_handler.cc

Issue 710513004: Adding GCM support to the copresence component. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Adding a unit test Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/copresence/rpc/rpc_handler.h" 5 #include "components/copresence/rpc/rpc_handler.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/command_line.h" 8 #include "base/command_line.h"
9 #include "base/guid.h" 9 #include "base/guid.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/strings/string_util.h" 11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h" 12 #include "base/strings/stringprintf.h"
13 13
14 // TODO(ckehoe): time.h includes windows.h, which #defines DeviceCapabilities 14 // TODO(ckehoe): time.h includes windows.h, which #defines DeviceCapabilities
15 // to DeviceCapabilitiesW. This breaks the pb.h headers below. For now, 15 // to DeviceCapabilitiesW. This breaks the pb.h headers below. For now,
16 // we fix this with an #undef. 16 // we fix this with an #undef.
17 #include "base/time/time.h" 17 #include "base/time/time.h"
18 #if defined(OS_WIN) 18 #if defined(OS_WIN)
19 #undef DeviceCapabilities 19 #undef DeviceCapabilities
20 #endif 20 #endif
21 21
22 #include "components/copresence/copresence_switches.h" 22 #include "components/copresence/copresence_switches.h"
23 #include "components/copresence/handlers/directive_handler.h" 23 #include "components/copresence/handlers/directive_handler.h"
24 #include "components/copresence/handlers/gcm_handler.h"
24 #include "components/copresence/proto/codes.pb.h" 25 #include "components/copresence/proto/codes.pb.h"
25 #include "components/copresence/proto/data.pb.h" 26 #include "components/copresence/proto/data.pb.h"
26 #include "components/copresence/proto/rpcs.pb.h" 27 #include "components/copresence/proto/rpcs.pb.h"
27 #include "components/copresence/public/copresence_constants.h" 28 #include "components/copresence/public/copresence_constants.h"
28 #include "components/copresence/public/copresence_delegate.h" 29 #include "components/copresence/public/copresence_delegate.h"
29 #include "components/copresence/rpc/http_post.h" 30 #include "components/copresence/rpc/http_post.h"
30 #include "net/http/http_status_code.h" 31 #include "net/http/http_status_code.h"
31 32
32 // TODO(ckehoe): Return error messages for bad requests. 33 // TODO(ckehoe): Return error messages for bad requests.
33 34
34 namespace copresence { 35 namespace copresence {
35 36
36 using google::protobuf::MessageLite; 37 using google::protobuf::MessageLite;
37 using google::protobuf::RepeatedPtrField; 38 using google::protobuf::RepeatedPtrField;
38 39
39 const char RpcHandler::kReportRequestRpcName[] = "report"; 40 const char RpcHandler::kReportRequestRpcName[] = "report";
40 41
41 // Number of characters of suffix to log for auth tokens 42 namespace {
42 const int kTokenSuffix = 5;
43 43
44 namespace { 44 const int kTokenLoggingSuffix = 5;
45 const int kInvalidTokenExpiryTimeMs = 10 * 60 * 1000; // 10 minutes.
46 const int kMaxInvalidTokens = 10000;
47 const char kRegisterDeviceRpcName[] = "registerdevice";
48 const char kDefaultCopresenceServer[] =
49 "https://www.googleapis.com/copresence/v2/copresence";
45 50
46 // UrlSafe is defined as: 51 // UrlSafe is defined as:
47 // '/' represented by a '_' and '+' represented by a '-' 52 // '/' represented by a '_' and '+' represented by a '-'
48 // TODO(rkc): Move this to the wrapper. 53 // TODO(rkc): Move this to the wrapper.
49 std::string ToUrlSafe(std::string token) { 54 std::string ToUrlSafe(std::string token) {
50 base::ReplaceChars(token, "+", "-", &token); 55 base::ReplaceChars(token, "+", "-", &token);
51 base::ReplaceChars(token, "/", "_", &token); 56 base::ReplaceChars(token, "/", "_", &token);
52 return token; 57 return token;
53 } 58 }
54 59
55 const int kInvalidTokenExpiryTimeMs = 10 * 60 * 1000; // 10 minutes.
56 const int kMaxInvalidTokens = 10000;
57 const char kRegisterDeviceRpcName[] = "registerdevice";
58 const char kDefaultCopresenceServer[] =
59 "https://www.googleapis.com/copresence/v2/copresence";
60 60
61 // Logging 61 // Logging
62 62
63 // Checks for a copresence error. If there is one, logs it and returns true. 63 // Checks for a copresence error. If there is one, logs it and returns true.
64 bool IsErrorStatus(const Status& status) { 64 bool IsErrorStatus(const Status& status) {
65 if (status.code() != OK) { 65 if (status.code() != OK) {
66 LOG(ERROR) << "Copresence error code " << status.code() 66 LOG(ERROR) << "Copresence error code " << status.code()
67 << (status.message().empty() ? "" : ": " + status.message()); 67 << (status.message().empty() ? "" : ": " + status.message());
68 } 68 }
69 return status.code() != OK; 69 return status.code() != OK;
(...skipping 17 matching lines...) Expand all
87 if (response.has_manage_messages_response()) 87 if (response.has_manage_messages_response())
88 LogIfErrorStatus(response.manage_messages_response().status(), "Publish"); 88 LogIfErrorStatus(response.manage_messages_response().status(), "Publish");
89 if (response.has_manage_subscriptions_response()) { 89 if (response.has_manage_subscriptions_response()) {
90 LogIfErrorStatus(response.manage_subscriptions_response().status(), 90 LogIfErrorStatus(response.manage_subscriptions_response().status(),
91 "Subscribe"); 91 "Subscribe");
92 } 92 }
93 93
94 return result; 94 return result;
95 } 95 }
96 96
97 const std::string LoggingStrForToken(const std::string& auth_token) {
98 std::string token_str = "anonymous";
99 if (!auth_token.empty()) {
100 std::string token_suffix = auth_token.substr(
101 auth_token.length() - kTokenLoggingSuffix, kTokenLoggingSuffix);
102 token_str = base::StringPrintf("token ...%s", token_suffix.c_str());
103 }
104 return token_str;
105 }
106
107
97 // Request construction 108 // Request construction
98 // TODO(ckehoe): Move these into a separate file? 109 // TODO(ckehoe): Move these into a separate file?
99 110
100 template <typename T> 111 template <typename T>
101 BroadcastScanConfiguration GetBroadcastScanConfig(const T& msg) { 112 BroadcastScanConfiguration GetBroadcastScanConfig(const T& msg) {
102 if (msg.has_token_exchange_strategy() && 113 if (msg.has_token_exchange_strategy() &&
103 msg.token_exchange_strategy().has_broadcast_scan_configuration()) { 114 msg.token_exchange_strategy().has_broadcast_scan_configuration()) {
104 return msg.token_exchange_strategy().broadcast_scan_configuration(); 115 return msg.token_exchange_strategy().broadcast_scan_configuration();
105 } 116 }
106 return BROADCAST_SCAN_CONFIGURATION_UNKNOWN; 117 return BROADCAST_SCAN_CONFIGURATION_UNKNOWN;
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
142 TokenObservation* token_observation = 153 TokenObservation* token_observation =
143 request->mutable_update_signals_request()->add_token_observation(); 154 request->mutable_update_signals_request()->add_token_observation();
144 token_observation->set_token_id(ToUrlSafe(token.token)); 155 token_observation->set_token_id(ToUrlSafe(token.token));
145 156
146 TokenSignals* signals = token_observation->add_signals(); 157 TokenSignals* signals = token_observation->add_signals();
147 signals->set_medium(token.audible ? AUDIO_AUDIBLE_DTMF 158 signals->set_medium(token.audible ? AUDIO_AUDIBLE_DTMF
148 : AUDIO_ULTRASOUND_PASSBAND); 159 : AUDIO_ULTRASOUND_PASSBAND);
149 signals->set_observed_time_millis(base::Time::Now().ToJsTime()); 160 signals->set_observed_time_millis(base::Time::Now().ToJsTime());
150 } 161 }
151 162
152 const std::string LoggingStrForToken(const std::string& auth_token) {
153 std::string token_str = auth_token.empty() ? "anonymous" :
154 base::StringPrintf("token ...%s",
155 auth_token.substr(auth_token.length() - kTokenSuffix,
156 kTokenSuffix).c_str());
157 return token_str;
158 }
159
160 } // namespace 163 } // namespace
161 164
162 165
163 // Public functions. 166 // Public functions.
164 167
165 RpcHandler::RpcHandler(CopresenceDelegate* delegate, 168 RpcHandler::RpcHandler(CopresenceDelegate* delegate,
166 DirectiveHandler* directive_handler, 169 DirectiveHandler* directive_handler,
170 GCMHandler* gcm_handler,
167 const PostCallback& server_post_callback) 171 const PostCallback& server_post_callback)
168 : delegate_(delegate), 172 : delegate_(delegate),
169 directive_handler_(directive_handler), 173 directive_handler_(directive_handler),
174 gcm_handler_(gcm_handler),
170 server_post_callback_(server_post_callback), 175 server_post_callback_(server_post_callback),
171 invalid_audio_token_cache_( 176 invalid_audio_token_cache_(
172 base::TimeDelta::FromMilliseconds(kInvalidTokenExpiryTimeMs), 177 base::TimeDelta::FromMilliseconds(kInvalidTokenExpiryTimeMs),
173 kMaxInvalidTokens) { 178 kMaxInvalidTokens) {
174 DCHECK(delegate_); 179 DCHECK(delegate_);
175 DCHECK(directive_handler_); 180 DCHECK(directive_handler_);
181 // |gcm_handler_| is optional.
176 182
177 if (server_post_callback_.is_null()) { 183 if (server_post_callback_.is_null()) {
178 server_post_callback_ = 184 server_post_callback_ =
179 base::Bind(&RpcHandler::SendHttpPost, base::Unretained(this)); 185 base::Bind(&RpcHandler::SendHttpPost, base::Unretained(this));
180 } 186 }
187
188 if (gcm_handler_) {
189 gcm_handler_->GetGcmId(
190 base::Bind(&RpcHandler::RegisterGcmId, base::Unretained(this)));
fgorski 2014/11/07 17:40:44 again, consider using WeakPtrFactory
rkc 2014/11/07 18:39:05 Weak pointers are heavily discouraged since they m
Charlie 2014/11/07 19:42:30 Acknowledged.
191 }
181 } 192 }
182 193
183 RpcHandler::~RpcHandler() { 194 RpcHandler::~RpcHandler() {
184 // Do not use |directive_handler_| here. 195 // Do not use |directive_handler_| here.
185 // It will already have been destructed. 196 // It will already have been destructed.
186 for (HttpPost* post : pending_posts_) 197 for (HttpPost* post : pending_posts_)
187 delete post; 198 delete post;
188 } 199 }
189 200
190 void RpcHandler::SendReportRequest(scoped_ptr<ReportRequest> request, 201 void RpcHandler::SendReportRequest(scoped_ptr<ReportRequest> request,
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
251 AddTokenToRequest(token, &request); 262 AddTokenToRequest(token, &request);
252 } 263 }
253 264
254 // Report under all active tokens. 265 // Report under all active tokens.
255 for (const auto& registration : device_id_by_auth_token_) { 266 for (const auto& registration : device_id_by_auth_token_) {
256 SendReportRequest(make_scoped_ptr(new ReportRequest(request)), 267 SendReportRequest(make_scoped_ptr(new ReportRequest(request)),
257 registration.first); 268 registration.first);
258 } 269 }
259 } 270 }
260 271
261 // Private methods 272
273 // Private functions.
262 274
263 RpcHandler::PendingRequest::PendingRequest(scoped_ptr<ReportRequest> report, 275 RpcHandler::PendingRequest::PendingRequest(scoped_ptr<ReportRequest> report,
264 const std::string& app_id, 276 const std::string& app_id,
265 const std::string& auth_token, 277 const std::string& auth_token,
266 const StatusCallback& callback) 278 const StatusCallback& callback)
267 : report(report.Pass()), 279 : report(report.Pass()),
268 app_id(app_id), 280 app_id(app_id),
269 auth_token(auth_token), 281 auth_token(auth_token),
270 callback(callback) {} 282 callback(callback) {}
271 283
272 RpcHandler::PendingRequest::~PendingRequest() {} 284 RpcHandler::PendingRequest::~PendingRequest() {}
273 285
274 void RpcHandler::RegisterForToken(const std::string& auth_token) { 286 void RpcHandler::RegisterForToken(const std::string& auth_token) {
275 DVLOG(2) << "Sending " << LoggingStrForToken(auth_token) 287 DVLOG(2) << "Sending " << LoggingStrForToken(auth_token)
276 << " registration to server."; 288 << " registration to server.";
277 289
278 // Mark registration as in progress. 290 scoped_ptr<RegisterDeviceRequest> request(new RegisterDeviceRequest);
279 device_id_by_auth_token_[auth_token] = "";
280 291
281 scoped_ptr<RegisterDeviceRequest> request(new RegisterDeviceRequest); 292 // Add a GCM ID for authenticated registration, if we have one.
282 request->mutable_push_service()->set_service(PUSH_SERVICE_NONE); 293 if (auth_token.empty() || gcm_id_.empty()) {
294 request->mutable_push_service()->set_service(PUSH_SERVICE_NONE);
295 } else {
296 DVLOG(2) << "Registering GCM ID with " << LoggingStrForToken(auth_token);
297 request->mutable_push_service()->set_service(GCM);
298 request->mutable_push_service()->mutable_gcm_registration()
299 ->set_device_token(gcm_id_);
300 }
283 301
284 // Only identify as a Chrome device if we're in anonymous mode. 302 // Only identify as a Chrome device if we're in anonymous mode.
285 // Authenticated calls come from a "GAIA device". 303 // Authenticated calls come from a "GAIA device".
286 if (auth_token.empty()) { 304 if (auth_token.empty()) {
287 Identity* identity = 305 Identity* identity =
288 request->mutable_device_identifiers()->mutable_registrant(); 306 request->mutable_device_identifiers()->mutable_registrant();
289 identity->set_type(CHROME); 307 identity->set_type(CHROME);
290 identity->set_chrome_id(base::GenerateGUID()); 308 identity->set_chrome_id(base::GenerateGUID());
309
310 // Since we're generating a new "Chrome ID" here,
311 // we need to make sure this isn't a duplicate registration.
312 DCHECK_EQ(0u, device_id_by_auth_token_.count(std::string()))
313 << "Attempted anonymous re-registration";
291 } 314 }
292 315
316 bool gcm_pending = !auth_token.empty() && gcm_handler_ && gcm_id_.empty();
293 SendServerRequest( 317 SendServerRequest(
294 kRegisterDeviceRpcName, 318 kRegisterDeviceRpcName,
295 std::string(), // device ID 319 // This will have the side effect of populating an empty device ID
320 // for this auth token in the map. This is what we want,
321 // to mark registration as being in progress.
322 device_id_by_auth_token_[auth_token],
296 std::string(), // app ID 323 std::string(), // app ID
297 auth_token, 324 auth_token,
298 request.Pass(), 325 request.Pass(),
299 base::Bind(&RpcHandler::RegisterResponseHandler, 326 base::Bind(&RpcHandler::RegisterResponseHandler,
300 // On destruction, this request will be cancelled. 327 // On destruction, this request will be cancelled.
301 base::Unretained(this), 328 base::Unretained(this),
302 auth_token)); 329 auth_token,
330 gcm_pending));
303 } 331 }
304 332
305 void RpcHandler::ProcessQueuedRequests(const std::string& auth_token) { 333 void RpcHandler::ProcessQueuedRequests(const std::string& auth_token) {
306 // Track requests that are not on this auth token. 334 // Track requests that are not on this auth token.
307 ScopedVector<PendingRequest> still_pending_requests; 335 ScopedVector<PendingRequest> still_pending_requests;
308 336
309 // If there is no device ID for this auth token, registration failed. 337 // If there is no device ID for this auth token, registration failed.
310 bool registration_failed = 338 bool registration_failed =
311 (device_id_by_auth_token_.count(auth_token) == 0); 339 (device_id_by_auth_token_.count(auth_token) == 0);
312 340
(...skipping 23 matching lines...) Expand all
336 } 364 }
337 365
338 void RpcHandler::SendReportRequest(scoped_ptr<ReportRequest> request, 366 void RpcHandler::SendReportRequest(scoped_ptr<ReportRequest> request,
339 const std::string& auth_token) { 367 const std::string& auth_token) {
340 SendReportRequest(request.Pass(), 368 SendReportRequest(request.Pass(),
341 std::string(), 369 std::string(),
342 auth_token, 370 auth_token,
343 StatusCallback()); 371 StatusCallback());
344 } 372 }
345 373
374 // Store a GCM ID and send it to the server if needed. The constructor passes
375 // this callback to the GCMHandler to receive the ID whenever it's ready.
376 // It may be returned immediately, if the ID is cached, or require a server
377 // round-trip. This ID must then be passed along to the copresence server.
378 // There are a few ways this can happen for each auth token:
379 //
380 // 1. The GCM ID is available when we first register, and is passed along
381 // with the RegisterDeviceRequest.
382 //
383 // 2. The GCM ID becomes available after the RegisterDeviceRequest has
384 // completed. Then the loop in this function will invoke RegisterForToken()
385 // again to pass on the ID.
386 //
387 // 3. The GCM ID becomes available after the RegisterDeviceRequest is sent,
388 // but before it completes. In this case, the gcm_pending flag is passed
389 // through to the RegisterResponseHandler, which invokes RegisterForToken()
390 // again to pass on the ID. The loop here must skip pending registrations,
391 // as the device ID will be empty.
392 //
393 // TODO(ckehoe): Add tests for these scenarios.
394 void RpcHandler::RegisterGcmId(const std::string& gcm_id) {
395 gcm_id_ = gcm_id;
396 if (!gcm_id.empty()) {
397 for (const auto& registration : device_id_by_auth_token_) {
398 const std::string& auth_token = registration.first;
399 const std::string& device_id = registration.second;
400 if (!auth_token.empty() && !device_id.empty())
401 RegisterForToken(auth_token);
402 }
403 }
404 }
405
346 void RpcHandler::RegisterResponseHandler( 406 void RpcHandler::RegisterResponseHandler(
347 const std::string& auth_token, 407 const std::string& auth_token,
408 bool gcm_pending,
348 HttpPost* completed_post, 409 HttpPost* completed_post,
349 int http_status_code, 410 int http_status_code,
350 const std::string& response_data) { 411 const std::string& response_data) {
351 if (completed_post) { 412 if (completed_post) {
352 int elements_erased = pending_posts_.erase(completed_post); 413 int elements_erased = pending_posts_.erase(completed_post);
353 DCHECK_GT(elements_erased, 0); 414 DCHECK_GT(elements_erased, 0);
354 delete completed_post; 415 delete completed_post;
355 } 416 }
356 417
357 // Registration is no longer in progress. 418 // Registration is no longer in progress.
358 // If it was successful, we'll update below. 419 // If it was successful, we'll update below.
359 device_id_by_auth_token_.erase(auth_token); 420 device_id_by_auth_token_.erase(auth_token);
360 421
361 RegisterDeviceResponse response; 422 RegisterDeviceResponse response;
362 if (http_status_code != net::HTTP_OK) { 423 if (http_status_code != net::HTTP_OK) {
363 // TODO(ckehoe): Retry registration if appropriate. 424 // TODO(ckehoe): Retry registration if appropriate.
364 LOG(ERROR) << LoggingStrForToken(auth_token) 425 LOG(ERROR) << LoggingStrForToken(auth_token)
365 << " device registration failed"; 426 << " device registration failed";
366 } else if (!response.ParseFromString(response_data)) { 427 } else if (!response.ParseFromString(response_data)) {
367 LOG(ERROR) << "Invalid RegisterDeviceResponse:\n" << response_data; 428 LOG(ERROR) << "Invalid RegisterDeviceResponse:\n" << response_data;
368 } else if (!IsErrorStatus(response.header().status())) { 429 } else if (!IsErrorStatus(response.header().status())) {
369 const std::string& device_id = response.registered_device_id(); 430 const std::string& device_id = response.registered_device_id();
370 DCHECK(!device_id.empty()); 431 DCHECK(!device_id.empty());
371 device_id_by_auth_token_[auth_token] = device_id; 432 device_id_by_auth_token_[auth_token] = device_id;
372 DVLOG(2) << LoggingStrForToken(auth_token) 433 DVLOG(2) << LoggingStrForToken(auth_token)
373 << " device registration successful. Id: " << device_id; 434 << " device registration successful. Id: " << device_id;
435
436 // If we have a GCM ID now, and didn't before, pass it on to the server.
437 if (gcm_pending && !gcm_id_.empty())
438 RegisterForToken(auth_token);
374 } 439 }
375 440
376 // Send or fail requests on this auth token. 441 // Send or fail requests on this auth token.
377 ProcessQueuedRequests(auth_token); 442 ProcessQueuedRequests(auth_token);
378 } 443 }
379 444
380 void RpcHandler::ReportResponseHandler(const StatusCallback& status_callback, 445 void RpcHandler::ReportResponseHandler(const StatusCallback& status_callback,
381 HttpPost* completed_post, 446 HttpPost* completed_post,
382 int http_status_code, 447 int http_status_code,
383 const std::string& response_data) { 448 const std::string& response_data) {
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after
567 api_key, 632 api_key,
568 auth_token, 633 auth_token,
569 command_line->GetSwitchValueASCII(switches::kCopresenceTracingToken), 634 command_line->GetSwitchValueASCII(switches::kCopresenceTracingToken),
570 *request_proto); 635 *request_proto);
571 636
572 http_post->Start(base::Bind(callback, http_post)); 637 http_post->Start(base::Bind(callback, http_post));
573 pending_posts_.insert(http_post); 638 pending_posts_.insert(http_post);
574 } 639 }
575 640
576 } // namespace copresence 641 } // namespace copresence
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698