OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 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 "net/sdch/sdch_owner.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/metrics/histogram_macros.h" | |
9 #include "base/time/default_clock.h" | |
10 #include "net/base/sdch_manager.h" | |
11 #include "net/base/sdch_net_log_params.h" | |
12 | |
13 namespace { | |
14 | |
15 enum DictionaryFate { | |
16 // A Get-Dictionary header wasn't acted on. | |
17 DICTIONARY_FATE_GET_IGNORED = 1, | |
18 | |
19 // A fetch was attempted, but failed. | |
20 // TODO(rdsmith): Actually record this case. | |
21 DICTIONARY_FATE_FETCH_FAILED = 2, | |
22 | |
23 // A successful fetch was dropped on the floor, no space. | |
24 DICTIONARY_FATE_FETCH_IGNORED_NO_SPACE = 3, | |
25 | |
26 // A successful fetch was refused by the SdchManager. | |
27 DICTIONARY_FATE_FETCH_MANAGER_REFUSED = 4, | |
28 | |
29 // A dictionary was successfully added. | |
30 DICTIONARY_FATE_ADDED = 5, | |
31 | |
32 // A dictionary was evicted by an incoming dict. | |
33 DICTIONARY_FATE_EVICT_FOR_DICT = 6, | |
34 | |
35 // A dictionary was evicted by memory pressure. | |
36 DICTIONARY_FATE_EVICT_FOR_MEMORY = 7, | |
37 | |
38 // A dictionary was evicted on destruction. | |
39 DICTIONARY_FATE_EVICT_FOR_DESTRUCTION = 8, | |
40 | |
41 DICTIONARY_FATE_MAX = 9 | |
42 }; | |
43 | |
44 void RecordDictionaryFate(enum DictionaryFate fate) { | |
45 UMA_HISTOGRAM_ENUMERATION("Sdch3.DictionaryFate", fate, DICTIONARY_FATE_MAX); | |
46 } | |
47 | |
48 void RecordDictionaryEviction(int use_count, DictionaryFate fate) { | |
49 DCHECK(fate == DICTIONARY_FATE_EVICT_FOR_DICT || | |
50 fate == DICTIONARY_FATE_EVICT_FOR_MEMORY || | |
51 fate == DICTIONARY_FATE_EVICT_FOR_DESTRUCTION); | |
52 | |
53 UMA_HISTOGRAM_COUNTS_100("Sdch3.DictionaryUseCount", use_count); | |
54 RecordDictionaryFate(fate); | |
55 } | |
56 | |
57 } // namespace | |
58 | |
59 namespace net { | |
60 | |
61 // Adjust SDCH limits downwards for mobile. | |
62 #if defined(OS_ANDROID) || defined(OS_IOS) | |
63 // static | |
64 const size_t SdchOwner::kMaxTotalDictionarySize = 500 * 1000; | |
65 #else | |
66 // static | |
67 const size_t SdchOwner::kMaxTotalDictionarySize = 20 * 1000 * 1000; | |
68 #endif | |
69 | |
70 // Somewhat arbitrary, but we assume a dictionary smaller than | |
71 // 50K isn't going to do anyone any good. Note that this still doesn't | |
72 // prevent download and addition unless there is less than this | |
73 // amount of space available in storage. | |
74 const size_t SdchOwner::kMinSpaceForDictionaryFetch = 50 * 1000; | |
75 | |
76 SdchOwner::SdchOwner(net::SdchManager* sdch_manager, | |
77 net::URLRequestContext* context) | |
78 : manager_(sdch_manager), | |
79 fetcher_(context, | |
80 base::Bind(&SdchOwner::OnDictionaryFetched, | |
81 // Because |fetcher_| is owned by SdchOwner, the | |
82 // SdchOwner object will be available for the lifetime | |
83 // of |fetcher_|. | |
84 base::Unretained(this))), | |
85 total_dictionary_bytes_(0), | |
86 clock_(new base::DefaultClock), | |
87 max_total_dictionary_size_(kMaxTotalDictionarySize), | |
88 min_space_for_dictionary_fetch_(kMinSpaceForDictionaryFetch), | |
89 memory_pressure_listener_( | |
90 base::Bind(&SdchOwner::OnMemoryPressure, | |
91 // Because |memory_pressure_listener_| is owned by | |
92 // SdchOwner, the SdchOwner object will be available | |
93 // for the lifetime // of |memory_pressure_listener_|. | |
Bence
2015/01/08 14:37:17
Remove extra // from middle of line.
Randy Smith (Not in Mondays)
2015/01/08 15:48:23
Ooops. Done.
| |
94 base::Unretained(this))) { | |
95 manager_->AddObserver(this); | |
96 } | |
97 | |
98 SdchOwner::~SdchOwner() { | |
99 for (auto it = local_dictionary_info_.begin(); | |
100 it != local_dictionary_info_.end(); ++it) { | |
101 RecordDictionaryEviction(it->second.use_count, | |
102 DICTIONARY_FATE_EVICT_FOR_DESTRUCTION); | |
103 } | |
104 manager_->RemoveObserver(this); | |
105 } | |
106 | |
107 void SdchOwner::SetMaxTotalDictionarySize(size_t max_total_dictionary_size) { | |
108 max_total_dictionary_size_ = max_total_dictionary_size; | |
109 } | |
110 | |
111 void SdchOwner::SetMinSpaceForDictionaryFetch( | |
112 size_t min_space_for_dictionary_fetch) { | |
113 min_space_for_dictionary_fetch_ = min_space_for_dictionary_fetch; | |
114 } | |
115 | |
116 void SdchOwner::OnDictionaryFetched(const std::string& dictionary_text, | |
117 const GURL& dictionary_url, | |
118 const net::BoundNetLog& net_log) { | |
119 struct DictionaryItem { | |
120 base::Time last_used; | |
121 std::string server_hash; | |
122 int use_count; | |
123 size_t dictionary_size; | |
124 | |
125 DictionaryItem() : use_count(0), dictionary_size(0) {} | |
126 DictionaryItem(const base::Time& last_used, | |
127 const std::string& server_hash, | |
128 int use_count, | |
129 size_t dictionary_size) | |
130 : last_used(last_used), | |
131 server_hash(server_hash), | |
132 use_count(use_count), | |
133 dictionary_size(dictionary_size) {} | |
134 DictionaryItem(const DictionaryItem& rhs) { | |
Bence
2015/01/08 14:37:17
Copy constructor is generated when needed, why do
Randy Smith (Not in Mondays)
2015/01/08 15:48:23
So I interpreted http://google-styleguide.googleco
Bence
2015/01/08 15:56:38
Excellent.
| |
135 last_used = rhs.last_used; | |
136 server_hash = rhs.server_hash; | |
137 use_count = rhs.use_count; | |
138 dictionary_size = rhs.dictionary_size; | |
139 } | |
140 DictionaryItem& operator=(const DictionaryItem& rhs) { | |
141 last_used = rhs.last_used; | |
142 server_hash = rhs.server_hash; | |
143 use_count = rhs.use_count; | |
144 dictionary_size = rhs.dictionary_size; | |
145 return *this; | |
146 } | |
147 bool operator<(const DictionaryItem& rhs) const { | |
148 return last_used < rhs.last_used; | |
149 } | |
150 }; | |
151 | |
152 std::vector<DictionaryItem> stale_dictionary_list; | |
153 size_t recoverable_bytes = 0; | |
154 base::Time stale_boundary(clock_->Now() - base::TimeDelta::FromDays(1)); | |
155 for (auto used_it = local_dictionary_info_.begin(); | |
156 used_it != local_dictionary_info_.end(); ++used_it) { | |
157 if (used_it->second.last_used < stale_boundary) { | |
158 stale_dictionary_list.push_back( | |
159 DictionaryItem(used_it->second.last_used, used_it->first, | |
160 used_it->second.use_count, used_it->second.size)); | |
161 recoverable_bytes += used_it->second.size; | |
162 } | |
163 } | |
164 | |
165 if (total_dictionary_bytes_ + dictionary_text.size() - recoverable_bytes > | |
166 max_total_dictionary_size_) { | |
167 RecordDictionaryFate(DICTIONARY_FATE_FETCH_IGNORED_NO_SPACE); | |
168 net::SdchManager::SdchErrorRecovery(SDCH_DICTIONARY_NO_ROOM); | |
169 net_log.AddEvent(net::NetLog::TYPE_SDCH_DICTIONARY_ERROR, | |
170 base::Bind(&net::NetLogSdchDictionaryFetchProblemCallback, | |
171 SDCH_DICTIONARY_NO_ROOM, dictionary_url, true)); | |
172 return; | |
173 } | |
174 | |
175 // Evict from oldest to youngest until we have space. | |
176 std::sort(stale_dictionary_list.begin(), stale_dictionary_list.end()); | |
177 size_t avail_bytes = max_total_dictionary_size_ - total_dictionary_bytes_; | |
178 auto stale_it = stale_dictionary_list.begin(); | |
179 while (avail_bytes < dictionary_text.size() && | |
180 stale_it != stale_dictionary_list.end()) { | |
181 manager_->RemoveSdchDictionary(stale_it->server_hash); | |
182 RecordDictionaryEviction(stale_it->use_count, | |
183 DICTIONARY_FATE_EVICT_FOR_DICT); | |
184 local_dictionary_info_.erase(stale_it->server_hash); | |
185 avail_bytes += stale_it->dictionary_size; | |
186 ++stale_it; | |
187 } | |
188 DCHECK(avail_bytes >= dictionary_text.size()); | |
189 | |
190 std::string server_hash; | |
191 net::SdchProblemCode rv = manager_->AddSdchDictionary( | |
192 dictionary_text, dictionary_url, &server_hash); | |
193 if (rv != net::SDCH_OK) { | |
194 RecordDictionaryFate(DICTIONARY_FATE_FETCH_MANAGER_REFUSED); | |
195 net::SdchManager::SdchErrorRecovery(rv); | |
196 net_log.AddEvent(net::NetLog::TYPE_SDCH_DICTIONARY_ERROR, | |
197 base::Bind(&net::NetLogSdchDictionaryFetchProblemCallback, | |
198 rv, dictionary_url, true)); | |
199 return; | |
200 } | |
201 | |
202 RecordDictionaryFate(DICTIONARY_FATE_ADDED); | |
203 | |
204 DCHECK(local_dictionary_info_.end() == | |
205 local_dictionary_info_.find(server_hash)); | |
206 total_dictionary_bytes_ += dictionary_text.size(); | |
207 local_dictionary_info_[server_hash] = DictionaryInfo( | |
208 // Set the time last used to something to avoid thrashing, but not recent, | |
209 // to avoid taking too much time/space with useless dictionaries/one-off | |
210 // visits to web sites. | |
211 clock_->Now() - base::TimeDelta::FromHours(23), dictionary_text.size()); | |
212 } | |
213 | |
214 void SdchOwner::OnDictionaryUsed(SdchManager* manager, | |
215 const std::string& server_hash) { | |
216 auto it = local_dictionary_info_.find(server_hash); | |
217 DCHECK(local_dictionary_info_.end() != it); | |
218 | |
219 it->second.last_used = clock_->Now(); | |
220 it->second.use_count++; | |
221 } | |
222 | |
223 void SdchOwner::OnGetDictionary(net::SdchManager* manager, | |
224 const GURL& request_url, | |
225 const GURL& dictionary_url) { | |
226 base::Time stale_boundary(clock_->Now() - base::TimeDelta::FromDays(1)); | |
227 size_t avail_bytes = 0; | |
228 for (auto it = local_dictionary_info_.begin(); | |
229 it != local_dictionary_info_.end(); ++it) { | |
230 if (it->second.last_used < stale_boundary) | |
231 avail_bytes += it->second.size; | |
232 } | |
233 | |
234 // Don't initiate the fetch if we wouldn't be able to store any | |
235 // reasonable dictionary. | |
236 // TODO(rdsmith): Maybe do a HEAD request to figure out how much | |
237 // storage we'd actually need? | |
238 if (max_total_dictionary_size_ < (total_dictionary_bytes_ - avail_bytes + | |
239 min_space_for_dictionary_fetch_)) { | |
240 RecordDictionaryFate(DICTIONARY_FATE_GET_IGNORED); | |
241 // TODO(rdsmith): Log a net-internals error. This requires | |
242 // SdchManager to forward the URLRequest that detected the | |
243 // Get-Dictionary header to its observers, which is tricky | |
244 // because SdchManager is layered underneath URLRequest. | |
245 return; | |
246 } | |
247 | |
248 fetcher_.Schedule(dictionary_url); | |
249 } | |
250 | |
251 void SdchOwner::OnClearDictionaries(net::SdchManager* manager) { | |
252 total_dictionary_bytes_ = 0; | |
253 local_dictionary_info_.clear(); | |
254 fetcher_.Cancel(); | |
255 } | |
256 | |
257 void SdchOwner::SetClockForTesting(scoped_ptr<base::Clock> clock) { | |
258 clock_ = clock.Pass(); | |
259 } | |
260 | |
261 void SdchOwner::OnMemoryPressure( | |
262 base::MemoryPressureListener::MemoryPressureLevel level) { | |
263 DCHECK_NE(base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE, level); | |
264 | |
265 for (auto it = local_dictionary_info_.begin(); | |
266 it != local_dictionary_info_.end(); ++it) { | |
267 RecordDictionaryEviction(it->second.use_count, | |
268 DICTIONARY_FATE_EVICT_FOR_MEMORY); | |
269 } | |
270 | |
271 // TODO(rdsmith): Make a distinction between moderate and critical | |
272 // memory pressure. | |
273 manager_->ClearData(); | |
274 } | |
275 | |
276 } // namespace net | |
OLD | NEW |