OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |