OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 // Library functions related to the Financial Server ping. | 5 // Library functions related to the Financial Server ping. |
6 | 6 |
7 #include "rlz/lib/financial_ping.h" | 7 #include "rlz/lib/financial_ping.h" |
8 | 8 |
9 #include <stdint.h> | 9 #include <stdint.h> |
10 | 10 |
11 #include <memory> | 11 #include <memory> |
12 | 12 |
13 #include "base/atomicops.h" | 13 #include "base/atomicops.h" |
14 #include "base/location.h" | 14 #include "base/location.h" |
15 #include "base/macros.h" | 15 #include "base/macros.h" |
16 #include "base/memory/weak_ptr.h" | 16 #include "base/memory/ref_counted.h" |
17 #include "base/single_thread_task_runner.h" | |
18 #include "base/strings/string_util.h" | 17 #include "base/strings/string_util.h" |
19 #include "base/strings/stringprintf.h" | 18 #include "base/strings/stringprintf.h" |
20 #include "base/strings/utf_string_conversions.h" | 19 #include "base/strings/utf_string_conversions.h" |
| 20 #include "base/synchronization/lock.h" |
| 21 #include "base/synchronization/waitable_event.h" |
| 22 #include "base/task_scheduler/post_task.h" |
21 #include "base/threading/thread_task_runner_handle.h" | 23 #include "base/threading/thread_task_runner_handle.h" |
22 #include "build/build_config.h" | 24 #include "build/build_config.h" |
23 #include "rlz/lib/assert.h" | 25 #include "rlz/lib/assert.h" |
24 #include "rlz/lib/lib_values.h" | 26 #include "rlz/lib/lib_values.h" |
25 #include "rlz/lib/machine_id.h" | 27 #include "rlz/lib/machine_id.h" |
26 #include "rlz/lib/rlz_lib.h" | 28 #include "rlz/lib/rlz_lib.h" |
27 #include "rlz/lib/rlz_value_store.h" | 29 #include "rlz/lib/rlz_value_store.h" |
28 #include "rlz/lib/string_utils.h" | 30 #include "rlz/lib/string_utils.h" |
29 | 31 |
30 #if !defined(OS_WIN) | 32 #if !defined(OS_WIN) |
(...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
196 | 198 |
197 bool FinancialPing::SetURLRequestContext( | 199 bool FinancialPing::SetURLRequestContext( |
198 net::URLRequestContextGetter* context) { | 200 net::URLRequestContextGetter* context) { |
199 base::subtle::Release_Store( | 201 base::subtle::Release_Store( |
200 &g_context, reinterpret_cast<AtomicWord>(context)); | 202 &g_context, reinterpret_cast<AtomicWord>(context)); |
201 return true; | 203 return true; |
202 } | 204 } |
203 | 205 |
204 namespace { | 206 namespace { |
205 | 207 |
| 208 // A waitable event used to detect when either: |
| 209 // |
| 210 // 1/ the RLZ ping request completes |
| 211 // 2/ the RLZ ping request times out |
| 212 // 3/ browser shutdown begins |
| 213 class RefCountedWaitableEvent |
| 214 : public base::RefCountedThreadSafe<RefCountedWaitableEvent> { |
| 215 public: |
| 216 RefCountedWaitableEvent() |
| 217 : event_(base::WaitableEvent::ResetPolicy::MANUAL, |
| 218 base::WaitableEvent::InitialState::NOT_SIGNALED) {} |
| 219 |
| 220 void SignalShutdown() { event_.Signal(); } |
| 221 |
| 222 void SignalFetchComplete(int response_code, std::string response) { |
| 223 base::AutoLock autolock(lock_); |
| 224 response_code_ = response_code; |
| 225 response_ = std::move(response); |
| 226 event_.Signal(); |
| 227 } |
| 228 |
| 229 bool TimedWait(base::TimeDelta timeout) { return event_.TimedWait(timeout); } |
| 230 |
| 231 int GetResponseCode() { |
| 232 base::AutoLock autolock(lock_); |
| 233 return response_code_; |
| 234 } |
| 235 |
| 236 std::string TakeResponse() { |
| 237 base::AutoLock autolock(lock_); |
| 238 std::string temp = std::move(response_); |
| 239 response_.clear(); |
| 240 return temp; |
| 241 } |
| 242 |
| 243 private: |
| 244 ~RefCountedWaitableEvent() {} |
| 245 friend class base::RefCountedThreadSafe<RefCountedWaitableEvent>; |
| 246 |
| 247 base::WaitableEvent event_; |
| 248 base::Lock lock_; |
| 249 std::string response_; |
| 250 int response_code_ = net::URLFetcher::RESPONSE_CODE_INVALID; |
| 251 }; |
| 252 |
| 253 // A fetcher delegate that signals an instance of RefCountedWaitableEvent when |
| 254 // the fetch completes. |
206 class FinancialPingUrlFetcherDelegate : public net::URLFetcherDelegate { | 255 class FinancialPingUrlFetcherDelegate : public net::URLFetcherDelegate { |
207 public: | 256 public: |
208 FinancialPingUrlFetcherDelegate(const base::Closure& callback) | 257 FinancialPingUrlFetcherDelegate(scoped_refptr<RefCountedWaitableEvent> event) |
209 : callback_(callback) { | 258 : event_(std::move(event)) {} |
| 259 |
| 260 void SetFetcher(std::unique_ptr<net::URLFetcher> fetcher) { |
| 261 fetcher_ = std::move(fetcher); |
210 } | 262 } |
211 void OnURLFetchComplete(const net::URLFetcher* source) override; | |
212 | 263 |
213 private: | 264 private: |
214 base::Closure callback_; | 265 void OnURLFetchComplete(const net::URLFetcher* source) override { |
| 266 std::string response; |
| 267 source->GetResponseAsString(&response); |
| 268 event_->SignalFetchComplete(source->GetResponseCode(), std::move(response)); |
| 269 base::SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); |
| 270 } |
| 271 |
| 272 scoped_refptr<RefCountedWaitableEvent> event_; |
| 273 std::unique_ptr<net::URLFetcher> fetcher_; |
215 }; | 274 }; |
216 | 275 |
217 void FinancialPingUrlFetcherDelegate::OnURLFetchComplete( | |
218 const net::URLFetcher* source) { | |
219 callback_.Run(); | |
220 } | |
221 | |
222 bool send_financial_ping_interrupted_for_test = false; | 276 bool send_financial_ping_interrupted_for_test = false; |
223 | 277 |
224 } // namespace | 278 } // namespace |
225 | 279 |
226 void ShutdownCheck(base::WeakPtr<base::RunLoop> weak) { | 280 void ShutdownCheck(scoped_refptr<RefCountedWaitableEvent> event) { |
227 if (!weak.get()) | |
228 return; | |
229 if (!base::subtle::Acquire_Load(&g_context)) { | 281 if (!base::subtle::Acquire_Load(&g_context)) { |
230 send_financial_ping_interrupted_for_test = true; | 282 send_financial_ping_interrupted_for_test = true; |
231 weak->QuitClosure().Run(); | 283 event->SignalShutdown(); |
232 return; | 284 return; |
233 } | 285 } |
234 // How frequently the financial ping thread should check | 286 // How frequently the financial ping thread should check |
235 // the shutdown condition? | 287 // the shutdown condition? |
236 const base::TimeDelta kInterval = base::TimeDelta::FromMilliseconds(500); | 288 const base::TimeDelta kInterval = base::TimeDelta::FromMilliseconds(500); |
237 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | 289 base::PostDelayedTaskWithTraits(FROM_HERE, {base::TaskPriority::BACKGROUND}, |
238 FROM_HERE, base::Bind(&ShutdownCheck, weak), kInterval); | 290 base::Bind(&ShutdownCheck, event), kInterval); |
| 291 } |
| 292 |
| 293 void PingRlzServer(std::string url, |
| 294 scoped_refptr<RefCountedWaitableEvent> event) { |
| 295 // Copy the pointer to stack because g_context may be set to NULL |
| 296 // in different thread. The instance is guaranteed to exist while |
| 297 // the method is running. |
| 298 net::URLRequestContextGetter* context = |
| 299 reinterpret_cast<net::URLRequestContextGetter*>( |
| 300 base::subtle::Acquire_Load(&g_context)); |
| 301 |
| 302 // Browser shutdown will cause the context to be reset to NULL. |
| 303 // ShutdownCheck will catch this. |
| 304 if (!context) |
| 305 return; |
| 306 |
| 307 // Delegate will delete itself when the fetch completes. |
| 308 FinancialPingUrlFetcherDelegate* delegate = |
| 309 new FinancialPingUrlFetcherDelegate(event); |
| 310 |
| 311 net::NetworkTrafficAnnotationTag traffic_annotation = |
| 312 net::DefineNetworkTrafficAnnotation("rlz_ping", R"( |
| 313 semantics { |
| 314 sender: "RLZ Ping" |
| 315 description: |
| 316 "Used for measuring the effectiveness of a promotion. See the " |
| 317 "Chrome Privacy Whitepaper for complete details." |
| 318 trigger: |
| 319 "1- At Chromium first run.\n" |
| 320 "2- When Chromium is re-activated by a new promotion.\n" |
| 321 "3- Once a week thereafter as long as Chromium is used.\n" |
| 322 data: |
| 323 "1- Non-unique cohort tag of when Chromium was installed.\n" |
| 324 "2- Unique machine id on desktop platforms.\n" |
| 325 "3- Whether Google is the default omnibox search.\n" |
| 326 "4- Whether google.com is the default home page." |
| 327 destination: GOOGLE_OWNED_SERVICE |
| 328 } |
| 329 policy { |
| 330 cookies_allowed: NO |
| 331 setting: "This feature cannot be disabled in settings." |
| 332 policy_exception_justification: "Not implemented." |
| 333 })"); |
| 334 std::unique_ptr<net::URLFetcher> fetcher = net::URLFetcher::Create( |
| 335 GURL(url), net::URLFetcher::GET, delegate, traffic_annotation); |
| 336 |
| 337 fetcher->SetLoadFlags( |
| 338 net::LOAD_DISABLE_CACHE | net::LOAD_DO_NOT_SEND_AUTH_DATA | |
| 339 net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES); |
| 340 |
| 341 // Ensure rlz_lib::SetURLRequestContext() has been called before sending |
| 342 // pings. |
| 343 fetcher->SetRequestContext(context); |
| 344 fetcher->Start(); |
| 345 |
| 346 // Pass ownership of the fetcher to the delegate. Otherwise the fetch will |
| 347 // be canceled when the URLFetcher object is destroyed. |
| 348 delegate->SetFetcher(std::move(fetcher)); |
239 } | 349 } |
240 #endif | 350 #endif |
241 | 351 |
242 bool FinancialPing::PingServer(const char* request, std::string* response) { | 352 bool FinancialPing::PingServer(const char* request, std::string* response) { |
243 if (!response) | 353 if (!response) |
244 return false; | 354 return false; |
245 | 355 |
246 response->clear(); | 356 response->clear(); |
247 | 357 |
248 #if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET) | 358 #if defined(RLZ_NETWORK_IMPLEMENTATION_WIN_INET) |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
289 | 399 |
290 DWORD bytes_read = 0; | 400 DWORD bytes_read = 0; |
291 while (InternetReadFile(http_handle, buffer.get(), kMaxPingResponseLength, | 401 while (InternetReadFile(http_handle, buffer.get(), kMaxPingResponseLength, |
292 &bytes_read) && bytes_read > 0) { | 402 &bytes_read) && bytes_read > 0) { |
293 response->append(buffer.get(), bytes_read); | 403 response->append(buffer.get(), bytes_read); |
294 bytes_read = 0; | 404 bytes_read = 0; |
295 }; | 405 }; |
296 | 406 |
297 return true; | 407 return true; |
298 #else | 408 #else |
299 // Copy the pointer to stack because g_context may be set to NULL | |
300 // in different thread. The instance is guaranteed to exist while | |
301 // the method is running. | |
302 net::URLRequestContextGetter* context = | |
303 reinterpret_cast<net::URLRequestContextGetter*>( | |
304 base::subtle::Acquire_Load(&g_context)); | |
305 | |
306 // Browser shutdown will cause the context to be reset to NULL. | |
307 if (!context) | |
308 return false; | |
309 | |
310 // Run a blocking event loop to match the win inet implementation. | |
311 std::unique_ptr<base::MessageLoop> message_loop; | |
312 // Ensure that we have a MessageLoop. | |
313 if (!base::MessageLoop::current()) | |
314 message_loop.reset(new base::MessageLoop); | |
315 base::RunLoop loop; | |
316 FinancialPingUrlFetcherDelegate delegate(loop.QuitClosure()); | |
317 | |
318 std::string url = base::StringPrintf("http://%s:%d%s", | 409 std::string url = base::StringPrintf("http://%s:%d%s", |
319 kFinancialServer, kFinancialPort, | 410 kFinancialServer, kFinancialPort, |
320 request); | 411 request); |
321 | 412 |
322 net::NetworkTrafficAnnotationTag traffic_annotation = | 413 // Use a waitable event to cause this function to block, to match the |
323 net::DefineNetworkTrafficAnnotation("rlz_ping", R"( | 414 // wininet implementation. |
324 semantics { | 415 auto event = base::MakeRefCounted<RefCountedWaitableEvent>(); |
325 sender: "RLZ Ping" | |
326 description: | |
327 "Used for measuring the effectiveness of a promotion. See the " | |
328 "Chrome Privacy Whitepaper for complete details." | |
329 trigger: | |
330 "1- At Chromium first run.\n" | |
331 "2- When Chromium is re-activated by a new promotion.\n" | |
332 "3- Once a week thereafter as long as Chromium is used.\n" | |
333 data: | |
334 "1- Non-unique cohort tag of when Chromium was installed.\n" | |
335 "2- Unique machine id on desktop platforms.\n" | |
336 "3- Whether Google is the default omnibox search.\n" | |
337 "4- Whether google.com is the default home page." | |
338 destination: GOOGLE_OWNED_SERVICE | |
339 } | |
340 policy { | |
341 cookies_allowed: NO | |
342 setting: "This feature cannot be disabled in settings." | |
343 policy_exception_justification: "Not implemented." | |
344 })"); | |
345 std::unique_ptr<net::URLFetcher> fetcher = net::URLFetcher::Create( | |
346 GURL(url), net::URLFetcher::GET, &delegate, traffic_annotation); | |
347 | 416 |
348 fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE | | 417 base::PostTaskWithTraits(FROM_HERE, {base::TaskPriority::BACKGROUND}, |
349 net::LOAD_DO_NOT_SEND_AUTH_DATA | | 418 base::Bind(&ShutdownCheck, event)); |
350 net::LOAD_DO_NOT_SEND_COOKIES | | |
351 net::LOAD_DO_NOT_SAVE_COOKIES); | |
352 | 419 |
353 // Ensure rlz_lib::SetURLRequestContext() has been called before sending | 420 // PingRlzServer must be run in a separate sequence so that the TimedWait() |
354 // pings. | 421 // call below does not block the URL fetch response from being handled by |
355 fetcher->SetRequestContext(context); | 422 // the URL delegate. |
| 423 scoped_refptr<base::SequencedTaskRunner> background_runner( |
| 424 base::CreateSequencedTaskRunnerWithTraits( |
| 425 {base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN, |
| 426 base::TaskPriority::BACKGROUND})); |
| 427 background_runner->PostTask(FROM_HERE, |
| 428 base::Bind(&PingRlzServer, url, event)); |
356 | 429 |
357 base::WeakPtrFactory<base::RunLoop> weak(&loop); | 430 bool is_signaled = event->TimedWait(base::TimeDelta::FromMinutes(5)); |
358 | 431 if (!is_signaled || event->GetResponseCode() != 200) |
359 const base::TimeDelta kTimeout = base::TimeDelta::FromMinutes(5); | |
360 base::MessageLoop::ScopedNestableTaskAllower allow_nested( | |
361 base::MessageLoop::current()); | |
362 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
363 FROM_HERE, base::Bind(&ShutdownCheck, weak.GetWeakPtr())); | |
364 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
365 FROM_HERE, | |
366 base::Bind(&net::URLFetcher::Start, base::Unretained(fetcher.get()))); | |
367 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
368 FROM_HERE, loop.QuitClosure(), kTimeout); | |
369 | |
370 loop.Run(); | |
371 | |
372 if (fetcher->GetResponseCode() != 200) | |
373 return false; | 432 return false; |
374 | 433 |
375 return fetcher->GetResponseAsString(response); | 434 *response = event->TakeResponse(); |
| 435 return true; |
376 #endif | 436 #endif |
377 } | 437 } |
378 | 438 |
379 bool FinancialPing::IsPingTime(Product product, bool no_delay) { | 439 bool FinancialPing::IsPingTime(Product product, bool no_delay) { |
380 ScopedRlzValueStoreLock lock; | 440 ScopedRlzValueStoreLock lock; |
381 RlzValueStore* store = lock.GetStore(); | 441 RlzValueStore* store = lock.GetStore(); |
382 if (!store || !store->HasAccess(RlzValueStore::kReadAccess)) | 442 if (!store || !store->HasAccess(RlzValueStore::kReadAccess)) |
383 return false; | 443 return false; |
384 | 444 |
385 int64_t last_ping = 0; | 445 int64_t last_ping = 0; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
431 } | 491 } |
432 | 492 |
433 bool WasSendFinancialPingInterrupted() { | 493 bool WasSendFinancialPingInterrupted() { |
434 return send_financial_ping_interrupted_for_test; | 494 return send_financial_ping_interrupted_for_test; |
435 } | 495 } |
436 | 496 |
437 } // namespace test | 497 } // namespace test |
438 #endif | 498 #endif |
439 | 499 |
440 } // namespace rlz_lib | 500 } // namespace rlz_lib |
OLD | NEW |