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 |
| 52 // failure. |
| 53 4, |
| 54 |
| 55 // Fuzzing percentage: 50% will spread delays randomly between 50%--100% |
| 56 // of |
| 57 // the nominal time. |
| 58 .5, // 50% |
| 59 |
| 60 // Maximum delay (in ms) during exponential backoff. |
| 61 6 * 3600 * 1000, // 6 hours |
| 62 |
| 63 // Time to keep an entry from being discarded even when it has no |
| 64 // significant state, -1 to never discard. (Not applicable.) |
| 65 -1, |
| 66 |
| 67 // False means that initial_delay_ms is the first delay once we start |
| 68 // exponential backoff, i.e., there is no delay after subsequent |
| 69 // successful |
| 70 // requests. |
| 71 false, |
| 72 }; |
| 73 |
| 74 // static |
| 75 const int64_t AffiliationFetchThrottler::kGracePeriodAfterReconnectMs = |
| 76 10 * 1000; // 10 seconds |
| 77 |
| 78 AffiliationFetchThrottler::AffiliationFetchThrottler( |
| 79 AffiliationFetchThrottlerDelegate* delegate, |
| 80 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| 81 scoped_ptr<base::TickClock> tick_clock) |
| 82 : delegate_(delegate), |
| 83 task_runner_(task_runner), |
| 84 state_(IDLE), |
| 85 has_network_connectivity_(false), |
| 86 is_fetch_scheduled_(false), |
| 87 tick_clock_(tick_clock.Pass()), |
| 88 exponential_backoff_( |
| 89 new BackoffEntryImpl(&kBackoffPolicy, tick_clock_.get())), |
| 90 weak_ptr_factory_(this) { |
| 91 DCHECK(delegate); |
| 92 // Start observing before querying the current connectivity state, so that if |
| 93 // the state changes concurrently in-between, it will not go unnoticed. |
| 94 net::NetworkChangeNotifier::AddConnectionTypeObserver(this); |
| 95 has_network_connectivity_ = !net::NetworkChangeNotifier::IsOffline(); |
| 96 } |
| 97 |
| 98 AffiliationFetchThrottler::~AffiliationFetchThrottler() { |
| 99 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); |
| 100 } |
| 101 |
| 102 void AffiliationFetchThrottler::SignalNetworkRequestNeeded() { |
| 103 if (state_ != IDLE) |
| 104 return; |
| 105 |
| 106 state_ = FETCH_NEEDED; |
| 107 if (has_network_connectivity_) |
| 108 EnsureCallbackIsScheduled(); |
| 109 } |
| 110 |
| 111 void AffiliationFetchThrottler::InformOfNetworkRequestComplete(bool success) { |
| 112 DCHECK_EQ(state_, FETCH_IN_FLIGHT); |
| 113 state_ = IDLE; |
| 114 exponential_backoff_->InformOfRequest(success); |
| 115 } |
| 116 |
| 117 void AffiliationFetchThrottler::EnsureCallbackIsScheduled() { |
| 118 DCHECK_EQ(state_, FETCH_NEEDED); |
| 119 DCHECK(has_network_connectivity_); |
| 120 |
| 121 if (is_fetch_scheduled_) |
| 122 return; |
| 123 |
| 124 is_fetch_scheduled_ = true; |
| 125 task_runner_->PostDelayedTask( |
| 126 FROM_HERE, |
| 127 base::Bind(&AffiliationFetchThrottler::OnBackoffDelayExpiredCallback, |
| 128 weak_ptr_factory_.GetWeakPtr()), |
| 129 exponential_backoff_->GetTimeUntilRelease()); |
| 130 } |
| 131 |
| 132 void AffiliationFetchThrottler::OnBackoffDelayExpiredCallback() { |
| 133 DCHECK_EQ(state_, FETCH_NEEDED); |
| 134 DCHECK(is_fetch_scheduled_); |
| 135 is_fetch_scheduled_ = false; |
| 136 |
| 137 // Do nothing if network connectivity was lost while this callback was in the |
| 138 // task queue. The callback will be posted in the OnConnectionTypeChanged |
| 139 // handler once again. |
| 140 if (!has_network_connectivity_) |
| 141 return; |
| 142 |
| 143 // The release time might have been increased if network connectivity was lost |
| 144 // and restored while this callback was in the task queue. If so, reschedule. |
| 145 if (exponential_backoff_->ShouldRejectRequest()) |
| 146 EnsureCallbackIsScheduled(); |
| 147 else |
| 148 state_ = delegate_->OnCanSendNetworkRequest() ? FETCH_IN_FLIGHT : IDLE; |
| 149 } |
| 150 |
| 151 void AffiliationFetchThrottler::OnConnectionTypeChanged( |
| 152 net::NetworkChangeNotifier::ConnectionType type) { |
| 153 bool old_has_network_connectivity = has_network_connectivity_; |
| 154 has_network_connectivity_ = |
| 155 (type != net::NetworkChangeNotifier::CONNECTION_NONE); |
| 156 |
| 157 // Only react when network connectivity has been reestablished. |
| 158 if (!has_network_connectivity_ || old_has_network_connectivity) |
| 159 return; |
| 160 |
| 161 double grace_ms = kGracePeriodAfterReconnectMs * |
| 162 (1 - base::RandDouble() * kBackoffPolicy.jitter_factor); |
| 163 exponential_backoff_->SetCustomReleaseTime(std::max( |
| 164 exponential_backoff_->GetReleaseTime(), |
| 165 tick_clock_->NowTicks() + base::TimeDelta::FromMillisecondsD(grace_ms))); |
| 166 |
| 167 if (state_ == FETCH_NEEDED) |
| 168 EnsureCallbackIsScheduled(); |
| 169 } |
| 170 |
| 171 } // namespace password_manager |
OLD | NEW |