OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 "components/password_manager/core/browser/affiliation_fetch_throttler.h
" |
| 6 |
| 7 #include <stdint.h> |
| 8 |
| 9 #include "base/logging.h" |
| 10 #include "base/rand_util.h" |
| 11 #include "base/thread_task_runner_handle.h" |
| 12 #include "base/time/tick_clock.h" |
| 13 #include "base/time/time.h" |
| 14 #include "components/password_manager/core/browser/affiliation_fetch_throttler_d
elegate.h" |
| 15 |
| 16 namespace password_manager { |
| 17 |
| 18 namespace { |
| 19 |
| 20 // Implementation of net::BackoffEntry that allows mocking its tick source. |
| 21 class BackoffEntryImpl : public net::BackoffEntry { |
| 22 public: |
| 23 // |tick_clock| must outlive this instance. |
| 24 explicit BackoffEntryImpl(const net::BackoffEntry::Policy* const policy, |
| 25 base::TickClock* tick_clock) |
| 26 : BackoffEntry(policy), tick_clock_(tick_clock) {} |
| 27 ~BackoffEntryImpl() override {} |
| 28 |
| 29 private: |
| 30 // net::BackoffEntry: |
| 31 base::TimeTicks ImplGetTimeNow() const override { |
| 32 return tick_clock_->NowTicks(); |
| 33 } |
| 34 |
| 35 base::TickClock* tick_clock_; |
| 36 |
| 37 DISALLOW_COPY_AND_ASSIGN(BackoffEntryImpl); |
| 38 }; |
| 39 |
| 40 } // namespace |
| 41 |
| 42 // static |
| 43 const net::BackoffEntry::Policy AffiliationFetchThrottler::kBackoffPolicy = { |
| 44 // Number of initial errors (in sequence) to ignore before going into |
| 45 // exponential backoff. |
| 46 0, |
| 47 |
| 48 // Initial delay (in ms) once backoff starts. |
| 49 10 * 1000, // 10 seconds |
| 50 |
| 51 // Factor by which the delay will be multiplied on each subsequent failure. |
| 52 4, |
| 53 |
| 54 // Fuzzing percentage: 50% will spread delays randomly between 50%--100% of |
| 55 // the nominal time. |
| 56 .5, // 50% |
| 57 |
| 58 // Maximum delay (in ms) during exponential backoff. |
| 59 6 * 3600 * 1000, // 6 hours |
| 60 |
| 61 // Time to keep an entry from being discarded even when it has no |
| 62 // significant state, -1 to never discard. (Not applicable.) |
| 63 -1, |
| 64 |
| 65 // False means that initial_delay_ms is the first delay once we start |
| 66 // exponential backoff, i.e., there is no delay after subsequent successful |
| 67 // requests. |
| 68 false, |
| 69 }; |
| 70 |
| 71 // static |
| 72 const int64_t AffiliationFetchThrottler::kGracePeriodAfterReconnectMs = |
| 73 10 * 1000; // 10 seconds |
| 74 |
| 75 AffiliationFetchThrottler::AffiliationFetchThrottler( |
| 76 AffiliationFetchThrottlerDelegate* delegate, |
| 77 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| 78 scoped_ptr<base::TickClock> tick_clock) |
| 79 : delegate_(delegate), |
| 80 task_runner_(task_runner), |
| 81 state_(IDLE), |
| 82 has_network_connectivity_(false), |
| 83 is_fetch_scheduled_(false), |
| 84 tick_clock_(tick_clock.Pass()), |
| 85 exponential_backoff_( |
| 86 new BackoffEntryImpl(&kBackoffPolicy, tick_clock_.get())), |
| 87 weak_ptr_factory_(this) { |
| 88 DCHECK(delegate); |
| 89 // Start observing before querying the current connectivity state, so that if |
| 90 // the state changes concurrently in-between, it will not go unnoticed. |
| 91 net::NetworkChangeNotifier::AddConnectionTypeObserver(this); |
| 92 has_network_connectivity_ = !net::NetworkChangeNotifier::IsOffline(); |
| 93 } |
| 94 |
| 95 AffiliationFetchThrottler::~AffiliationFetchThrottler() { |
| 96 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); |
| 97 } |
| 98 |
| 99 void AffiliationFetchThrottler::SignalNetworkRequestNeeded() { |
| 100 if (state_ != IDLE) |
| 101 return; |
| 102 |
| 103 state_ = FETCH_NEEDED; |
| 104 if (has_network_connectivity_) |
| 105 EnsureCallbackIsScheduled(); |
| 106 } |
| 107 |
| 108 void AffiliationFetchThrottler::InformOfNetworkRequestComplete(bool success) { |
| 109 DCHECK_EQ(state_, FETCH_IN_FLIGHT); |
| 110 state_ = IDLE; |
| 111 exponential_backoff_->InformOfRequest(success); |
| 112 } |
| 113 |
| 114 void AffiliationFetchThrottler::EnsureCallbackIsScheduled() { |
| 115 DCHECK_EQ(state_, FETCH_NEEDED); |
| 116 DCHECK(has_network_connectivity_); |
| 117 |
| 118 if (is_fetch_scheduled_) |
| 119 return; |
| 120 |
| 121 is_fetch_scheduled_ = true; |
| 122 task_runner_->PostDelayedTask( |
| 123 FROM_HERE, |
| 124 base::Bind(&AffiliationFetchThrottler::OnBackoffDelayExpiredCallback, |
| 125 weak_ptr_factory_.GetWeakPtr()), |
| 126 exponential_backoff_->GetTimeUntilRelease()); |
| 127 } |
| 128 |
| 129 void AffiliationFetchThrottler::OnBackoffDelayExpiredCallback() { |
| 130 DCHECK_EQ(state_, FETCH_NEEDED); |
| 131 DCHECK(is_fetch_scheduled_); |
| 132 is_fetch_scheduled_ = false; |
| 133 |
| 134 // Do nothing if network connectivity was lost while this callback was in the |
| 135 // task queue. The callback will be posted in the OnConnectionTypeChanged |
| 136 // handler once again. |
| 137 if (!has_network_connectivity_) |
| 138 return; |
| 139 |
| 140 // The release time might have been increased if network connectivity was lost |
| 141 // and restored while this callback was in the task queue. If so, reschedule. |
| 142 if (exponential_backoff_->ShouldRejectRequest()) |
| 143 EnsureCallbackIsScheduled(); |
| 144 else |
| 145 state_ = delegate_->OnCanSendNetworkRequest() ? FETCH_IN_FLIGHT : IDLE; |
| 146 } |
| 147 |
| 148 void AffiliationFetchThrottler::OnConnectionTypeChanged( |
| 149 net::NetworkChangeNotifier::ConnectionType type) { |
| 150 bool old_has_network_connectivity = has_network_connectivity_; |
| 151 has_network_connectivity_ = |
| 152 (type != net::NetworkChangeNotifier::CONNECTION_NONE); |
| 153 |
| 154 // Only react when network connectivity has been reestablished. |
| 155 if (!has_network_connectivity_ || old_has_network_connectivity) |
| 156 return; |
| 157 |
| 158 double grace_ms = kGracePeriodAfterReconnectMs * |
| 159 (1 - base::RandDouble() * kBackoffPolicy.jitter_factor); |
| 160 exponential_backoff_->SetCustomReleaseTime(std::max( |
| 161 exponential_backoff_->GetReleaseTime(), |
| 162 tick_clock_->NowTicks() + base::TimeDelta::FromMillisecondsD(grace_ms))); |
| 163 |
| 164 if (state_ == FETCH_NEEDED) |
| 165 EnsureCallbackIsScheduled(); |
| 166 } |
| 167 |
| 168 } // namespace password_manager |
OLD | NEW |