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 #include "net/base/backoff_entry.h" | |
16 | |
17 namespace password_manager { | |
18 | |
19 namespace { | |
20 | |
21 const net::BackoffEntry::Policy kAffiliationBackoffParameters = { | |
22 // Number of initial errors (in sequence) to ignore before going into | |
23 // exponential backoff. | |
24 0, | |
25 | |
26 // Initial delay (in ms) once backoff starts. | |
27 10 * 1000, // 10 seconds | |
28 | |
29 // Factor by which the delay will be multiplied on each subsequent failure. | |
30 4, | |
31 | |
32 // Fuzzing percentage: 50% will spread delays randomly between 50%--100% of | |
33 // the nominal time. | |
34 .5, // 50% | |
35 | |
36 // Maximum delay (in ms) during exponential backoff. | |
37 6 * 3600 * 1000, // 6 hours | |
38 | |
39 // Time to keep an entry from being discarded even when it has no | |
40 // significant state, -1 to never discard. (Not applicable.) | |
41 -1, | |
42 | |
43 // False means that initial_delay_ms is the first delay once we start | |
44 // exponential backoff, i.e., there is no delay after subsequent successful | |
45 // requests. | |
46 false, | |
47 }; | |
48 | |
49 // Grace period before the first request is sent after network connectivity is | |
50 // restored. The fuzzing factor from above applies. | |
51 const int64_t kGracePeriodAfterNetworkRestoredInMs = 10 * 1000; // 10 seconds | |
52 | |
53 // Implementation of net::BackoffEntry that allows mocking its tick source. | |
54 class BackoffEntryImpl : public net::BackoffEntry { | |
55 public: | |
56 explicit BackoffEntryImpl(base::TickClock* tick_clock) | |
57 : BackoffEntry(&kAffiliationBackoffParameters), tick_clock_(tick_clock) {} | |
58 ~BackoffEntryImpl() override {} | |
59 | |
60 private: | |
61 // net::BackoffEntry: | |
62 base::TimeTicks ImplGetTimeNow() const override { | |
63 return tick_clock_->NowTicks(); | |
64 } | |
65 | |
66 // Must outlive this instance. | |
67 base::TickClock* tick_clock_; | |
68 | |
69 DISALLOW_COPY_AND_ASSIGN(BackoffEntryImpl); | |
70 }; | |
71 | |
72 } // namespace | |
73 | |
74 AffiliationFetchThrottler::AffiliationFetchThrottler( | |
75 AffiliationFetchThrottlerDelegate* delegate, | |
76 scoped_refptr<base::SingleThreadTaskRunner> task_runner, | |
77 scoped_ptr<base::TickClock> tick_clock) | |
78 : delegate_(delegate), | |
79 task_runner_(task_runner), | |
80 state_(IDLE), | |
81 has_network_connectivity_(false), | |
82 is_fetch_scheduled_(false), | |
83 tick_clock_(tick_clock.Pass()), | |
84 exponential_backoff_(new BackoffEntryImpl(tick_clock_.get())), | |
85 weak_ptr_factory_(this) { | |
86 DCHECK(delegate); | |
87 net::NetworkChangeNotifier::AddConnectionTypeObserver(this); | |
88 has_network_connectivity_ = !net::NetworkChangeNotifier::IsOffline(); | |
mmenke
2015/01/16 21:28:34
Think it's worth a comment why this shouldn't be i
engedy
2015/01/19 18:13:11
Done.
| |
89 } | |
90 | |
91 AffiliationFetchThrottler::~AffiliationFetchThrottler() { | |
92 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); | |
93 } | |
94 | |
95 void AffiliationFetchThrottler::SignalNetworkRequestNeeded() { | |
96 if (state_ != IDLE) | |
mmenke
2015/01/16 21:28:35
Should we DCHECK on this? I assume one class will
engedy
2015/01/19 18:13:10
Being able to call this multiple times allows the
| |
97 return; | |
98 | |
99 state_ = FETCH_NEEDED; | |
mmenke
2015/01/16 21:28:35
Does the delegate have to be able to handle a call
mmenke
2015/01/16 21:49:54
Hrm...Was thinking there was a case where that cou
engedy
2015/01/19 18:13:11
Yes, I believe this can never happen. I have added
| |
100 if (has_network_connectivity_) | |
101 EnsureCallbackIsScheduled(); | |
102 } | |
103 | |
104 void AffiliationFetchThrottler::InformOfNetworkRequestComplete(bool success) { | |
105 DCHECK_EQ(state_, FETCH_IN_FLIGHT); | |
mmenke
2015/01/16 21:28:34
Suggest making expected value the first argument (
engedy
2015/01/19 18:13:11
I'd prefer to keep it this way. As opposed to EXPE
| |
106 state_ = IDLE; | |
107 exponential_backoff_->InformOfRequest(success); | |
mmenke
2015/01/16 21:31:28
It's up to the consumer to tell us if/when they wa
engedy
2015/01/19 18:13:11
Done. PTAL at the class comment.
| |
108 } | |
109 | |
110 void AffiliationFetchThrottler::EnsureCallbackIsScheduled() { | |
111 DCHECK_EQ(state_, FETCH_NEEDED); | |
mmenke
2015/01/16 21:49:54
DCHECK(has_network_connectivity_)?
engedy
2015/01/19 18:13:11
Done.
| |
112 if (is_fetch_scheduled_) | |
113 return; | |
114 | |
115 is_fetch_scheduled_ = true; | |
mmenke
2015/01/16 21:28:34
Suggest using a OneShotTimer instead, it keeps tra
engedy
2015/01/19 18:13:11
I wanted to use that, but it does not allow time t
mmenke
2015/01/21 17:18:52
Ah, I wasn't aware we had a task runner class with
| |
116 task_runner_->PostDelayedTask( | |
117 FROM_HERE, | |
118 base::Bind(&AffiliationFetchThrottler::OnBackoffDelayExpiredCallback, | |
119 weak_ptr_factory_.GetWeakPtr()), | |
120 exponential_backoff_->GetTimeUntilRelease()); | |
mmenke
2015/01/16 21:49:54
Also, do we need async behavior here? Seems like
engedy
2015/01/19 18:13:11
While we could certainly do that, do you think put
mmenke
2015/01/21 17:18:52
ScheduleCallbackIfNeeded? (Could even have the st
engedy
2015/01/23 22:09:43
I would like to keep always async behavior. On tha
| |
121 } | |
122 | |
123 void AffiliationFetchThrottler::OnBackoffDelayExpiredCallback() { | |
124 DCHECK_EQ(state_, FETCH_NEEDED); | |
125 DCHECK(is_fetch_scheduled_); | |
126 is_fetch_scheduled_ = false; | |
127 | |
128 // Do nothing if network connectivity was lost while this callback was in the | |
129 // task queue. The callback will be posted in the OnConnectionTypeChanged | |
130 // handler once again. | |
131 if (!has_network_connectivity_) | |
132 return; | |
133 | |
134 // The release time might have been increased if network connectivity was lost | |
135 // and restored while this callback was in the task queue. If so, reschedule. | |
136 if (exponential_backoff_->ShouldRejectRequest()) | |
137 EnsureCallbackIsScheduled(); | |
138 else | |
139 state_ = delegate_->OnCanSendNetworkRequest() ? FETCH_IN_FLIGHT : IDLE; | |
140 } | |
141 | |
142 void AffiliationFetchThrottler::OnConnectionTypeChanged( | |
143 net::NetworkChangeNotifier::ConnectionType type) { | |
144 bool old_has_network_connectivity = has_network_connectivity_; | |
145 has_network_connectivity_ = | |
146 (type != net::NetworkChangeNotifier::CONNECTION_NONE); | |
147 | |
148 // Only react when network connectivity has been reestablished. | |
149 if (!has_network_connectivity_ || old_has_network_connectivity) | |
150 return; | |
151 | |
152 double grace_ms = | |
153 kGracePeriodAfterNetworkRestoredInMs * | |
154 (1 - base::RandDouble() * kAffiliationBackoffParameters.jitter_factor); | |
155 base::TimeTicks release_time = std::max( | |
156 exponential_backoff_->GetReleaseTime(), | |
157 tick_clock_->NowTicks() + base::TimeDelta::FromMillisecondsD(grace_ms)); | |
158 // Note that SetCustomReleaseTime() takes the maximum of the current release | |
159 // time and the supplied |release_time|. | |
160 exponential_backoff_->SetCustomReleaseTime(release_time); | |
161 | |
162 if (state_ == FETCH_NEEDED) | |
163 EnsureCallbackIsScheduled(); | |
164 } | |
165 | |
166 } // namespace password_manager | |
OLD | NEW |