OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 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 #include "components/password_manager/core/browser/affiliation_backend.h" | 5 #include "components/password_manager/core/browser/affiliation_backend.h" |
6 | 6 |
7 #include <stdint.h> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/location.h" | |
7 #include "base/task_runner.h" | 11 #include "base/task_runner.h" |
12 #include "base/threading/thread_checker.h" | |
13 #include "base/time/clock.h" | |
14 #include "base/time/time.h" | |
15 #include "components/password_manager/core/browser/affiliation_database.h" | |
16 #include "components/password_manager/core/browser/affiliation_fetcher.h" | |
17 #include "net/url_request/url_request_context_getter.h" | |
8 | 18 |
9 namespace password_manager { | 19 namespace password_manager { |
10 | 20 namespace { |
11 AffiliationBackend::AffiliationBackend() { | 21 |
22 // The duration after which cached affiliation data is considered stale and will | |
23 // not be used to serve requests any longer. | |
24 const int64_t kCacheHardExpiryDurationInHours = 24; | |
Garrett Casto
2015/02/03 08:20:37
Why is this int64_t? Seems like overkill, especial
engedy
2015/02/03 10:49:23
It used to be in seconds, and I did not notice Tim
| |
25 | |
26 // RequestInfo ---------------------------------------------------------------- | |
27 | |
28 // Encapsulates the details of a pending GetAffiliations() request. | |
29 struct RequestInfo { | |
30 RequestInfo(); | |
31 ~RequestInfo(); | |
32 | |
33 AffiliationService::ResultCallback callback; | |
34 scoped_refptr<base::TaskRunner> callback_task_runner; | |
35 }; | |
36 | |
37 RequestInfo::RequestInfo() { | |
38 } | |
39 | |
40 RequestInfo::~RequestInfo() { | |
41 } | |
42 | |
43 } // namespace | |
44 | |
45 // AffiliationBackend::FacetManager ------------------------------------------- | |
46 | |
47 // Manages and performs ongoing tasks concerning a single facet. | |
48 class AffiliationBackend::FacetManager { | |
49 public: | |
50 // The |backend| must outlive this object. | |
51 FacetManager(AffiliationBackend* backend, const FacetURI& facet_uri); | |
52 ~FacetManager(); | |
53 | |
54 // Called when |affiliation| information regarding this facet has just been | |
55 // fetched from the Affiliation API. | |
56 void OnFetchSucceeded(const AffiliatedFacetsWithUpdateTime& affiliation); | |
57 | |
58 // Returns whether this instance has becomes redundant, that is, it has no | |
59 // more meaningful state than a newly created instance would have. | |
60 bool CanBeDiscarded() const; | |
61 | |
62 // Returns whether or not affiliation information relating to this facet needs | |
63 // to be fetched right now. | |
64 bool DoesRequireFetch() const; | |
65 | |
66 // Facet-specific implementations for methods in AffiliationService of the | |
67 // same name. See documentation in affiliation_service.h for details: | |
68 void GetAffiliations(bool cached_only, const RequestInfo& request_info); | |
69 | |
70 private: | |
71 // Returns the time when cached data for this facet will become stale. | |
72 base::Time GetCacheHardExpiryTime() const; | |
Garrett Casto
2015/02/03 08:20:38
Why do you call this HardExpiry instead of just Ex
engedy
2015/02/03 10:49:23
Yeah, there will be such a distinction, introduced
| |
73 | |
74 // Returns whether or not the cache has fresh data for this facet. | |
75 bool IsCachedDataFresh() const; | |
76 | |
77 // Posts the callback of the request described by |request_info| with success. | |
78 static void ServeRequestWithSuccess(const RequestInfo& request_info, | |
79 const AffiliatedFacets& affiliation); | |
80 | |
81 // Posts the callback of the request described by |request_info| with failure. | |
82 static void ServeRequestWithFailure(const RequestInfo& request_info); | |
83 | |
84 AffiliationBackend* backend_; | |
85 FacetURI facet_uri_; | |
86 | |
87 // The last time affiliation information was fetched for this facet, i.e. the | |
88 // freshness of the data in the database. If there is no corresponding data in | |
89 // in the database, this will contain something sufficiently far away in the | |
90 // past to be considered stale for sure. Otherwise, the last_update_time in | |
91 // the database should match this value; it is only cached to reduce disk I/O. | |
92 base::Time last_update_time_; | |
93 | |
94 // Contains information about the GetAffiliations() requests that are waiting | |
95 // for the result of looking up this facet. | |
96 std::vector<RequestInfo> pending_requests_; | |
97 | |
98 DISALLOW_COPY_AND_ASSIGN(FacetManager); | |
99 }; | |
100 | |
101 AffiliationBackend::FacetManager::FacetManager(AffiliationBackend* backend, | |
102 const FacetURI& facet_uri) | |
103 : backend_(backend), | |
104 facet_uri_(facet_uri), | |
105 last_update_time_(backend_->ReadLastUpdateTimeFromDatabase(facet_uri)) { | |
106 } | |
107 | |
108 AffiliationBackend::FacetManager::~FacetManager() { | |
109 // The manager will only be destroyed while there are pending requests if the | |
110 // entire backend is going. Call failure on pending requests in this case. | |
111 for (const auto& request_info : pending_requests_) | |
112 ServeRequestWithFailure(request_info); | |
113 } | |
114 | |
115 void AffiliationBackend::FacetManager::GetAffiliations( | |
116 bool cached_only, | |
117 const RequestInfo& request_info) { | |
118 if (IsCachedDataFresh()) { | |
119 AffiliatedFacetsWithUpdateTime affiliation; | |
120 if (!backend_->ReadAffiliationsFromDatabase(facet_uri_, &affiliation)) { | |
121 // TODO(engedy): Implement this. crbug.com/437865. | |
122 NOTIMPLEMENTED(); | |
123 } | |
124 DCHECK_EQ(affiliation.last_update_time, last_update_time_) << facet_uri_; | |
125 ServeRequestWithSuccess(request_info, affiliation.facets); | |
126 } else if (cached_only) { | |
127 ServeRequestWithFailure(request_info); | |
128 } else { | |
129 pending_requests_.push_back(request_info); | |
130 backend_->SignalNeedNetworkRequest(); | |
131 } | |
132 } | |
133 | |
134 void AffiliationBackend::FacetManager::OnFetchSucceeded( | |
135 const AffiliatedFacetsWithUpdateTime& affiliation) { | |
136 last_update_time_ = affiliation.last_update_time; | |
137 DCHECK(IsCachedDataFresh()) << facet_uri_; | |
138 for (const auto& request_info : pending_requests_) | |
139 ServeRequestWithSuccess(request_info, affiliation.facets); | |
140 pending_requests_.clear(); | |
141 } | |
142 | |
143 bool AffiliationBackend::FacetManager::CanBeDiscarded() const { | |
144 return pending_requests_.empty(); | |
145 } | |
146 | |
147 bool AffiliationBackend::FacetManager::DoesRequireFetch() const { | |
148 return !pending_requests_.empty() && !IsCachedDataFresh(); | |
149 } | |
150 | |
151 base::Time AffiliationBackend::FacetManager::GetCacheHardExpiryTime() const { | |
152 return last_update_time_ + | |
153 base::TimeDelta::FromHours(kCacheHardExpiryDurationInHours); | |
154 } | |
155 | |
156 bool AffiliationBackend::FacetManager::IsCachedDataFresh() const { | |
157 return backend_->GetCurrentTime() < GetCacheHardExpiryTime(); | |
158 } | |
159 | |
160 // static | |
161 void AffiliationBackend::FacetManager::ServeRequestWithSuccess( | |
162 const RequestInfo& request_info, | |
163 const AffiliatedFacets& affiliation) { | |
164 request_info.callback_task_runner->PostTask( | |
165 FROM_HERE, base::Bind(request_info.callback, affiliation, true)); | |
166 } | |
167 | |
168 // static | |
169 void AffiliationBackend::FacetManager::ServeRequestWithFailure( | |
170 const RequestInfo& request_info) { | |
171 request_info.callback_task_runner->PostTask( | |
172 FROM_HERE, base::Bind(request_info.callback, AffiliatedFacets(), false)); | |
173 } | |
174 | |
175 // AffiliationBackend --------------------------------------------------------- | |
176 | |
177 AffiliationBackend::AffiliationBackend( | |
178 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter, | |
179 scoped_ptr<base::Clock> time_source) | |
180 : request_context_getter_(request_context_getter), | |
181 clock_(time_source.Pass()), | |
182 weak_ptr_factory_(this) { | |
12 } | 183 } |
13 | 184 |
14 AffiliationBackend::~AffiliationBackend() { | 185 AffiliationBackend::~AffiliationBackend() { |
15 } | 186 } |
16 | 187 |
17 void AffiliationBackend::Initialize() { | 188 void AffiliationBackend::Initialize(const base::FilePath& db_path) { |
18 NOTIMPLEMENTED(); | 189 thread_checker_.reset(new base::ThreadChecker); |
190 cache_.reset(new AffiliationDatabase()); | |
191 if (!cache_->Init(db_path)) { | |
192 // TODO(engedy): Implement this. crbug.com/437865. | |
193 NOTIMPLEMENTED(); | |
194 } | |
19 } | 195 } |
20 | 196 |
21 void AffiliationBackend::GetAffiliations( | 197 void AffiliationBackend::GetAffiliations( |
22 const FacetURI& facet_uri, | 198 const FacetURI& facet_uri, |
23 bool cached_only, | 199 bool cached_only, |
24 const AffiliationService::ResultCallback& callback, | 200 const AffiliationService::ResultCallback& callback, |
25 scoped_refptr<base::TaskRunner> callback_task_runner) { | 201 const scoped_refptr<base::TaskRunner>& callback_task_runner) { |
26 NOTIMPLEMENTED(); | 202 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); |
203 if (!facet_managers_.contains(facet_uri)) { | |
204 scoped_ptr<FacetManager> new_manager(new FacetManager(this, facet_uri)); | |
205 facet_managers_.add(facet_uri, new_manager.Pass()); | |
206 } | |
207 | |
208 FacetManager* facet_manager = facet_managers_.get(facet_uri); | |
209 DCHECK(facet_manager); | |
210 RequestInfo request_info; | |
211 request_info.callback = callback; | |
212 request_info.callback_task_runner = callback_task_runner; | |
213 facet_manager->GetAffiliations(cached_only, request_info); | |
214 | |
215 if (facet_manager->CanBeDiscarded()) | |
216 facet_managers_.erase(facet_uri); | |
27 } | 217 } |
28 | 218 |
29 void AffiliationBackend::Prefetch(const FacetURI& facet_uri, | 219 void AffiliationBackend::Prefetch(const FacetURI& facet_uri, |
30 const base::Time& keep_fresh_until) { | 220 const base::Time& keep_fresh_until) { |
221 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); | |
222 | |
223 // TODO(engedy): Implement this. crbug.com/437865. | |
31 NOTIMPLEMENTED(); | 224 NOTIMPLEMENTED(); |
32 } | 225 } |
33 | 226 |
34 void AffiliationBackend::CancelPrefetch(const FacetURI& facet_uri, | 227 void AffiliationBackend::CancelPrefetch(const FacetURI& facet_uri, |
35 const base::Time& keep_fresh_until) { | 228 const base::Time& keep_fresh_until) { |
229 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); | |
230 | |
231 // TODO(engedy): Implement this. crbug.com/437865. | |
36 NOTIMPLEMENTED(); | 232 NOTIMPLEMENTED(); |
37 } | 233 } |
38 | 234 |
39 void AffiliationBackend::TrimCache() { | 235 void AffiliationBackend::TrimCache() { |
40 NOTIMPLEMENTED(); | 236 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); |
41 } | 237 |
42 | 238 // TODO(engedy): Implement this. crbug.com/437865. |
239 NOTIMPLEMENTED(); | |
240 } | |
241 | |
242 void AffiliationBackend::SendNetworkRequest() { | |
243 DCHECK(!fetcher_); | |
244 | |
245 std::vector<FacetURI> requested_facet_uris; | |
246 for (const auto& facet_manager_pair : facet_managers_) { | |
247 if (facet_manager_pair.second->DoesRequireFetch()) | |
248 requested_facet_uris.push_back(facet_manager_pair.first); | |
249 } | |
250 DCHECK(!requested_facet_uris.empty()); | |
251 fetcher_.reset(AffiliationFetcher::Create(request_context_getter_.get(), | |
252 requested_facet_uris, this)); | |
253 fetcher_->StartRequest(); | |
254 } | |
255 | |
256 base::Time AffiliationBackend::GetCurrentTime() { | |
257 return clock_->Now(); | |
258 } | |
259 | |
260 base::Time AffiliationBackend::ReadLastUpdateTimeFromDatabase( | |
261 const FacetURI& facet_uri) { | |
262 AffiliatedFacetsWithUpdateTime affiliation; | |
263 return ReadAffiliationsFromDatabase(facet_uri, &affiliation) | |
264 ? affiliation.last_update_time | |
265 : base::Time() - | |
266 base::TimeDelta::FromHours(kCacheHardExpiryDurationInHours); | |
Garrett Casto
2015/02/03 08:20:37
From reading the other comments it sounds like you
engedy
2015/02/03 10:49:23
Done.
| |
267 } | |
268 | |
269 bool AffiliationBackend::ReadAffiliationsFromDatabase( | |
270 const FacetURI& facet_uri, | |
271 AffiliatedFacetsWithUpdateTime* affiliations) { | |
272 return cache_->GetAffiliationsForFacet(facet_uri, affiliations); | |
273 } | |
274 | |
275 void AffiliationBackend::SignalNeedNetworkRequest() { | |
276 // TODO(engedy): Add more sophisticated throttling logic. crbug.com/437865. | |
277 if (fetcher_) | |
278 return; | |
279 SendNetworkRequest(); | |
280 } | |
281 | |
282 void AffiliationBackend::OnFetchSucceeded( | |
283 scoped_ptr<AffiliationFetcherDelegate::Result> result) { | |
284 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); | |
285 fetcher_.reset(); | |
286 | |
287 for (const AffiliatedFacets& affiliated_facets : *result) { | |
288 AffiliatedFacetsWithUpdateTime affiliation; | |
289 affiliation.facets = affiliated_facets; | |
290 affiliation.last_update_time = clock_->Now(); | |
291 | |
292 std::vector<AffiliatedFacetsWithUpdateTime> obsoleted_affiliations; | |
293 cache_->StoreAndRemoveConflicting(affiliation, &obsoleted_affiliations); | |
294 | |
295 // Cached data in contradiction with newly stored data automatically gets | |
296 // removed from the DB, and will be stored into |obsoleted_affiliations|. | |
297 // Let facet managers know if data is removed from under them. | |
298 // TODO(engedy): Implement this. crbug.com/437865. | |
299 if (!obsoleted_affiliations.empty()) | |
300 NOTIMPLEMENTED(); | |
301 | |
302 for (const auto& facet_uri : affiliated_facets) { | |
303 if (!facet_managers_.contains(facet_uri)) | |
304 continue; | |
305 FacetManager* facet_manager = facet_managers_.get(facet_uri); | |
306 facet_manager->OnFetchSucceeded(affiliation); | |
307 if (facet_manager->CanBeDiscarded()) | |
308 facet_managers_.erase(facet_uri); | |
309 } | |
310 } | |
311 | |
312 // A subsequent fetch may be needed if any additional GetAffiliations() | |
313 // requests came in while the current fetch was in flight. | |
314 for (const auto& facet_manager_pair : facet_managers_) { | |
315 if (facet_manager_pair.second->DoesRequireFetch()) { | |
316 SendNetworkRequest(); | |
317 return; | |
318 } | |
319 } | |
320 } | |
321 | |
322 void AffiliationBackend::OnFetchFailed() { | |
323 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); | |
324 | |
325 // TODO(engedy): Implement this. crbug.com/437865. | |
326 NOTIMPLEMENTED(); | |
327 } | |
328 | |
329 void AffiliationBackend::OnMalformedResponse() { | |
330 DCHECK(thread_checker_ && thread_checker_->CalledOnValidThread()); | |
331 | |
332 // TODO(engedy): Implement this. crbug.com/437865. | |
333 NOTIMPLEMENTED(); | |
334 } | |
335 | |
43 } // namespace password_manager | 336 } // namespace password_manager |
OLD | NEW |