Index: components/password_manager/core/browser/affiliation_fetch_throttler.cc |
diff --git a/components/password_manager/core/browser/affiliation_fetch_throttler.cc b/components/password_manager/core/browser/affiliation_fetch_throttler.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4d13f0b0db6cea5a2fe8dafc9a19a56c36cd0bd5 |
--- /dev/null |
+++ b/components/password_manager/core/browser/affiliation_fetch_throttler.cc |
@@ -0,0 +1,162 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "components/password_manager/core/browser/affiliation_fetch_throttler.h" |
+ |
+#include "base/logging.h" |
+#include "base/rand_util.h" |
+#include "base/thread_task_runner_handle.h" |
+#include "base/time/tick_clock.h" |
+#include "base/time/time.h" |
+ |
+namespace password_manager { |
+ |
+namespace { |
+ |
+net::BackoffEntry::Policy kAffiliationBackoffParameters = { |
mmenke
2015/01/14 19:25:07
const
engedy
2015/01/15 19:16:42
Done.
|
+ // Number of initial errors (in sequence) to ignore before going into |
+ // exponential backoff. |
+ 0, |
+ |
+ // Initial delay (in ms) once backoff starts. |
+ 10 * 1000, // 10 seconds |
+ |
+ // Factor by which the delay will be multiplied on each subsequent failure. |
+ 4, |
+ |
+ // Fuzzing percentage: 50% will spread delays randomly between 50%--100% of |
+ // the nominal time. |
+ .5, // 50% |
+ |
+ // Maximum delay (in ms) during exponential backoff. |
+ 6 * 3600 * 1000, // 6 hours |
+ |
+ // Time to keep an entry from being discarded even when it has no |
+ // significant state, -1 to never discard. (Not applicable.) |
+ -1, |
+ |
+ // False means that initial_delay_ms is the first delay once we start |
+ // exponential backoff, i.e., there is no delay after subsequent successful |
+ // requests. |
+ false, |
+}; |
+ |
+// Grace period before the first request is sent after network connectivity is |
+// restored. The fuzzing factor from above applies. |
+const int64 kGracePeriodAfterNetworkRestoredInMs = 10 * 1000; // 10 seconds |
mmenke
2015/01/14 19:25:07
Should include basictypes.h for int64
engedy
2015/01/15 19:16:42
Ahh, it looks like that is already deprecated. I h
|
+ |
+// Implementation of net::BackoffEntry that allows mocking its tick source. |
+class BackoffEntryImpl : public net::BackoffEntry { |
+ public: |
+ BackoffEntryImpl(base::TickClock* tick_clock) |
mmenke
2015/01/14 19:25:07
explicit
engedy
2015/01/15 19:16:42
Done.
|
+ : BackoffEntry(&kAffiliationBackoffParameters), tick_clock_(tick_clock) {} |
+ ~BackoffEntryImpl() override{}; |
mmenke
2015/01/14 19:25:07
nit: space after "override"
engedy
2015/01/15 19:16:42
Ahh, actually, there is also a superfluous semi-co
|
+ |
+ protected: |
+ // net::BackoffEntry: |
+ base::TimeTicks ImplGetTimeNow() const override { |
mmenke
2015/01/14 19:25:06
optional: This can be private (parent classes can
engedy
2015/01/15 19:16:42
Done.
|
+ return tick_clock_->NowTicks(); |
+ } |
+ |
+ private: |
+ // Must outlive this instance. |
+ base::TickClock* tick_clock_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(BackoffEntryImpl); |
+}; |
+ |
+} // namespace |
+ |
+AffiliationFetchThrottler::AffiliationFetchThrottler( |
+ AffiliationFetchThrottlerDelegate* delegate, |
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner, |
+ scoped_ptr<base::TickClock> tick_clock) |
+ : delegate_(delegate), |
+ task_runner_(task_runner), |
+ state_(IDLE), |
+ has_network_connectivity_(false), |
+ is_fetch_scheduled_(false), |
+ tick_clock_(tick_clock.Pass()), |
+ exponential_backoff_(new BackoffEntryImpl(tick_clock_.get())), |
+ weak_ptr_factory_(this) { |
+ DCHECK(delegate); |
+ net::NetworkChangeNotifier::AddConnectionTypeObserver(this); |
mmenke
2015/01/14 19:25:06
Putting this in a constructor seems like a bad ide
engedy
2015/01/15 19:16:42
I think that is a bit over-cautious. The contract
|
+ has_network_connectivity_ = !net::NetworkChangeNotifier::IsOffline(); |
+} |
+ |
+AffiliationFetchThrottler::~AffiliationFetchThrottler() { |
+ net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); |
+} |
+ |
+void AffiliationFetchThrottler::SignalNetworkRequestNeeded() { |
+ if (state_ != IDLE) |
+ return; |
+ |
+ state_ = FETCH_NEEDED; |
+ if (has_network_connectivity_) |
+ EnsureCallbackIsScheduled(); |
+} |
+ |
+void AffiliationFetchThrottler::InformOfNetworkRequestComplete(bool success) { |
+ DCHECK_EQ(state_, FETCH_IN_FLIGHT); |
+ state_ = IDLE; |
+ exponential_backoff_->InformOfRequest(success); |
mmenke
2015/01/14 20:43:43
Oops...Got so sidetracked thinking about the NCN s
engedy
2015/01/15 19:16:42
Please see my other comment.
|
+} |
+ |
+void AffiliationFetchThrottler::EnsureCallbackIsScheduled() { |
+ DCHECK_EQ(state_, FETCH_NEEDED); |
+ if (is_fetch_scheduled_) |
+ return; |
+ |
+ is_fetch_scheduled_ = true; |
+ task_runner_->PostDelayedTask( |
+ FROM_HERE, |
+ base::Bind(&AffiliationFetchThrottler::OnBackoffDelayExpiredCallback, |
+ weak_ptr_factory_.GetWeakPtr()), |
+ exponential_backoff_->GetTimeUntilRelease()); |
+} |
+ |
+void AffiliationFetchThrottler::OnBackoffDelayExpiredCallback() { |
+ DCHECK_EQ(state_, FETCH_NEEDED); |
+ DCHECK(is_fetch_scheduled_); |
+ is_fetch_scheduled_ = false; |
+ |
+ // Do nothing if network connectivity was lost while this callback was in the |
+ // task queue. The callback will be posted in the OnConnectionTypeChanged |
+ // handler once again. |
+ if (!has_network_connectivity_) |
+ return; |
+ |
+ // The release time might have been increased if network connectivity was lost |
+ // and restored while this callback was in the task queue. If so, reschedule. |
+ if (exponential_backoff_->ShouldRejectRequest()) |
+ EnsureCallbackIsScheduled(); |
+ else |
+ state_ = delegate_->OnCanSendNetworkRequest() ? FETCH_IN_FLIGHT : IDLE; |
+} |
+ |
+void AffiliationFetchThrottler::OnConnectionTypeChanged( |
+ net::NetworkChangeNotifier::ConnectionType type) { |
+ if (has_network_connectivity_ == !net::NetworkChangeNotifier::IsOffline()) |
mmenke
2015/01/14 19:25:06
What if we're switching from WIFI to cellular?
mmenke
2015/01/14 19:25:07
Shouldn't call NetworkChangeNotifier::IsOffline tw
engedy
2015/01/15 19:16:42
Done.
engedy
2015/01/15 19:16:42
Hmm. So far I did not consider the kind of the con
mmenke
2015/01/16 21:28:34
Now that I realize how this class actually works,
|
+ return; |
+ |
+ has_network_connectivity_ = !net::NetworkChangeNotifier::IsOffline(); |
+ if (!has_network_connectivity_) |
+ return; |
mmenke
2015/01/14 19:25:06
While being offline is one of the most common caus
mmenke
2015/01/14 19:25:07
No provisions to retry here?
engedy
2015/01/15 19:16:42
I am not sure I understand. Are you implying that
engedy
2015/01/15 19:16:42
Hmm. I believe this is somewhat different than net
mmenke
2015/01/16 21:28:34
Sorry, got confused about how this would plug in (
|
+ |
+ double grace_ms = |
+ kGracePeriodAfterNetworkRestoredInMs * |
+ (1 - base::RandDouble() * kAffiliationBackoffParameters.jitter_factor); |
+ base::TimeTicks release_time = std::max( |
+ exponential_backoff_->GetReleaseTime(), |
+ tick_clock_->NowTicks() + base::TimeDelta::FromMillisecondsD(grace_ms)); |
+ // Note that SetCustomReleaseTime() takes the maximum of the current release |
+ // time and the supplied |release_time|. |
+ exponential_backoff_->SetCustomReleaseTime(release_time); |
+ |
+ if (state_ == FETCH_NEEDED) |
+ EnsureCallbackIsScheduled(); |
+} |
+ |
+} // namespace password_manager |