• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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 "chrome/browser/net/http_server_properties_manager.h"
6 
7 #include "base/bind.h"
8 #include "base/metrics/histogram.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/rand_util.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/values.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/common/pref_names.h"
17 #include "components/pref_registry/pref_registry_syncable.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/notification_details.h"
20 #include "content/public/browser/notification_source.h"
21 
22 using content::BrowserThread;
23 
24 namespace chrome_browser_net {
25 
26 namespace {
27 
28 // Time to wait before starting an update the http_server_properties_impl_ cache
29 // from preferences. Scheduling another update during this period will reset the
30 // timer.
31 const int64 kUpdateCacheDelayMs = 1000;
32 
33 // Time to wait before starting an update the preferences from the
34 // http_server_properties_impl_ cache. Scheduling another update during this
35 // period will reset the timer.
36 const int64 kUpdatePrefsDelayMs = 5000;
37 
38 // "version" 0 indicates, http_server_properties doesn't have "version"
39 // property.
40 const int kMissingVersion = 0;
41 
42 // The version number of persisted http_server_properties.
43 const int kVersionNumber = 3;
44 
45 typedef std::vector<std::string> StringVector;
46 
47 // Load either 200 or 1000 servers based on a coin flip.
48 const int k200AlternateProtocolHostsToLoad = 200;
49 const int k1000AlternateProtocolHostsToLoad = 1000;
50 // Persist 1000 MRU AlternateProtocolHostPortPairs.
51 const int kMaxAlternateProtocolHostsToPersist = 1000;
52 
53 // Persist 200 MRU SpdySettingsHostPortPairs.
54 const int kMaxSpdySettingsHostsToPersist = 200;
55 
56 // Persist 300 MRU SupportsSpdyServerHostPortPairs.
57 const int kMaxSupportsSpdyServerHostsToPersist = 300;
58 
59 }  // namespace
60 
61 ////////////////////////////////////////////////////////////////////////////////
62 //  HttpServerPropertiesManager
63 
HttpServerPropertiesManager(PrefService * pref_service)64 HttpServerPropertiesManager::HttpServerPropertiesManager(
65     PrefService* pref_service)
66     : pref_service_(pref_service),
67       setting_prefs_(false) {
68   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
69   DCHECK(pref_service);
70   ui_weak_ptr_factory_.reset(
71       new base::WeakPtrFactory<HttpServerPropertiesManager>(this));
72   ui_weak_ptr_ = ui_weak_ptr_factory_->GetWeakPtr();
73   ui_cache_update_timer_.reset(
74       new base::OneShotTimer<HttpServerPropertiesManager>);
75   pref_change_registrar_.Init(pref_service_);
76   pref_change_registrar_.Add(
77       prefs::kHttpServerProperties,
78       base::Bind(&HttpServerPropertiesManager::OnHttpServerPropertiesChanged,
79                  base::Unretained(this)));
80 }
81 
~HttpServerPropertiesManager()82 HttpServerPropertiesManager::~HttpServerPropertiesManager() {
83   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
84   io_weak_ptr_factory_.reset();
85 }
86 
InitializeOnIOThread()87 void HttpServerPropertiesManager::InitializeOnIOThread() {
88   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
89   io_weak_ptr_factory_.reset(
90       new base::WeakPtrFactory<HttpServerPropertiesManager>(this));
91   http_server_properties_impl_.reset(new net::HttpServerPropertiesImpl());
92 
93   io_prefs_update_timer_.reset(
94       new base::OneShotTimer<HttpServerPropertiesManager>);
95 
96   BrowserThread::PostTask(
97       BrowserThread::UI,
98       FROM_HERE,
99       base::Bind(&HttpServerPropertiesManager::UpdateCacheFromPrefsOnUI,
100                  ui_weak_ptr_));
101 }
102 
ShutdownOnUIThread()103 void HttpServerPropertiesManager::ShutdownOnUIThread() {
104   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
105   // Cancel any pending updates, and stop listening for pref change updates.
106   ui_cache_update_timer_->Stop();
107   ui_weak_ptr_factory_.reset();
108   pref_change_registrar_.RemoveAll();
109 }
110 
111 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * prefs)112 void HttpServerPropertiesManager::RegisterProfilePrefs(
113     user_prefs::PrefRegistrySyncable* prefs) {
114   prefs->RegisterDictionaryPref(
115       prefs::kHttpServerProperties,
116       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
117 }
118 
119 // static
SetVersion(base::DictionaryValue * http_server_properties_dict,int version_number)120 void HttpServerPropertiesManager::SetVersion(
121     base::DictionaryValue* http_server_properties_dict,
122     int version_number) {
123   if (version_number < 0)
124     version_number =  kVersionNumber;
125   DCHECK_LE(version_number, kVersionNumber);
126   if (version_number <= kVersionNumber)
127     http_server_properties_dict->SetInteger("version", version_number);
128 }
129 
130 // This is required for conformance with the HttpServerProperties interface.
131 base::WeakPtr<net::HttpServerProperties>
GetWeakPtr()132     HttpServerPropertiesManager::GetWeakPtr() {
133   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
134   return io_weak_ptr_factory_->GetWeakPtr();
135 }
136 
Clear()137 void HttpServerPropertiesManager::Clear() {
138   Clear(base::Closure());
139 }
140 
Clear(const base::Closure & completion)141 void HttpServerPropertiesManager::Clear(const base::Closure& completion) {
142   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
143 
144   http_server_properties_impl_->Clear();
145   UpdatePrefsFromCacheOnIO(completion);
146 }
147 
SupportsSpdy(const net::HostPortPair & server)148 bool HttpServerPropertiesManager::SupportsSpdy(
149     const net::HostPortPair& server) {
150   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
151   return http_server_properties_impl_->SupportsSpdy(server);
152 }
153 
SetSupportsSpdy(const net::HostPortPair & server,bool support_spdy)154 void HttpServerPropertiesManager::SetSupportsSpdy(
155     const net::HostPortPair& server,
156     bool support_spdy) {
157   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
158 
159   http_server_properties_impl_->SetSupportsSpdy(server, support_spdy);
160   ScheduleUpdatePrefsOnIO();
161 }
162 
HasAlternateProtocol(const net::HostPortPair & server)163 bool HttpServerPropertiesManager::HasAlternateProtocol(
164     const net::HostPortPair& server) {
165   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
166   return http_server_properties_impl_->HasAlternateProtocol(server);
167 }
168 
169 net::PortAlternateProtocolPair
GetAlternateProtocol(const net::HostPortPair & server)170 HttpServerPropertiesManager::GetAlternateProtocol(
171     const net::HostPortPair& server) {
172   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
173   return http_server_properties_impl_->GetAlternateProtocol(server);
174 }
175 
SetAlternateProtocol(const net::HostPortPair & server,uint16 alternate_port,net::AlternateProtocol alternate_protocol)176 void HttpServerPropertiesManager::SetAlternateProtocol(
177     const net::HostPortPair& server,
178     uint16 alternate_port,
179     net::AlternateProtocol alternate_protocol) {
180   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
181   http_server_properties_impl_->SetAlternateProtocol(
182       server, alternate_port, alternate_protocol);
183   ScheduleUpdatePrefsOnIO();
184 }
185 
SetBrokenAlternateProtocol(const net::HostPortPair & server)186 void HttpServerPropertiesManager::SetBrokenAlternateProtocol(
187     const net::HostPortPair& server) {
188   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
189   http_server_properties_impl_->SetBrokenAlternateProtocol(server);
190   ScheduleUpdatePrefsOnIO();
191 }
192 
WasAlternateProtocolRecentlyBroken(const net::HostPortPair & server)193 bool HttpServerPropertiesManager::WasAlternateProtocolRecentlyBroken(
194     const net::HostPortPair& server) {
195   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
196   return http_server_properties_impl_->WasAlternateProtocolRecentlyBroken(
197       server);
198 }
199 
ConfirmAlternateProtocol(const net::HostPortPair & server)200 void HttpServerPropertiesManager::ConfirmAlternateProtocol(
201     const net::HostPortPair& server) {
202   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
203   http_server_properties_impl_->ConfirmAlternateProtocol(server);
204   ScheduleUpdatePrefsOnIO();
205 }
206 
ClearAlternateProtocol(const net::HostPortPair & server)207 void HttpServerPropertiesManager::ClearAlternateProtocol(
208     const net::HostPortPair& server) {
209   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
210   http_server_properties_impl_->ClearAlternateProtocol(server);
211   ScheduleUpdatePrefsOnIO();
212 }
213 
214 const net::AlternateProtocolMap&
alternate_protocol_map() const215 HttpServerPropertiesManager::alternate_protocol_map() const {
216   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
217   return http_server_properties_impl_->alternate_protocol_map();
218 }
219 
SetAlternateProtocolExperiment(net::AlternateProtocolExperiment experiment)220 void HttpServerPropertiesManager::SetAlternateProtocolExperiment(
221     net::AlternateProtocolExperiment experiment) {
222   http_server_properties_impl_->SetAlternateProtocolExperiment(experiment);
223 }
224 
225 net::AlternateProtocolExperiment
GetAlternateProtocolExperiment() const226 HttpServerPropertiesManager::GetAlternateProtocolExperiment() const {
227   return http_server_properties_impl_->GetAlternateProtocolExperiment();
228 }
229 
230 const net::SettingsMap&
GetSpdySettings(const net::HostPortPair & host_port_pair)231 HttpServerPropertiesManager::GetSpdySettings(
232     const net::HostPortPair& host_port_pair) {
233   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
234   return http_server_properties_impl_->GetSpdySettings(host_port_pair);
235 }
236 
SetSpdySetting(const net::HostPortPair & host_port_pair,net::SpdySettingsIds id,net::SpdySettingsFlags flags,uint32 value)237 bool HttpServerPropertiesManager::SetSpdySetting(
238     const net::HostPortPair& host_port_pair,
239     net::SpdySettingsIds id,
240     net::SpdySettingsFlags flags,
241     uint32 value) {
242   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
243   bool persist = http_server_properties_impl_->SetSpdySetting(
244       host_port_pair, id, flags, value);
245   if (persist)
246     ScheduleUpdatePrefsOnIO();
247   return persist;
248 }
249 
ClearSpdySettings(const net::HostPortPair & host_port_pair)250 void HttpServerPropertiesManager::ClearSpdySettings(
251     const net::HostPortPair& host_port_pair) {
252   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
253   http_server_properties_impl_->ClearSpdySettings(host_port_pair);
254   ScheduleUpdatePrefsOnIO();
255 }
256 
ClearAllSpdySettings()257 void HttpServerPropertiesManager::ClearAllSpdySettings() {
258   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
259   http_server_properties_impl_->ClearAllSpdySettings();
260   ScheduleUpdatePrefsOnIO();
261 }
262 
263 const net::SpdySettingsMap&
spdy_settings_map() const264 HttpServerPropertiesManager::spdy_settings_map() const {
265   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
266   return http_server_properties_impl_->spdy_settings_map();
267 }
268 
SetServerNetworkStats(const net::HostPortPair & host_port_pair,NetworkStats stats)269 void HttpServerPropertiesManager::SetServerNetworkStats(
270     const net::HostPortPair& host_port_pair,
271     NetworkStats stats) {
272   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
273   http_server_properties_impl_->SetServerNetworkStats(host_port_pair, stats);
274 }
275 
276 const HttpServerPropertiesManager::NetworkStats*
GetServerNetworkStats(const net::HostPortPair & host_port_pair) const277 HttpServerPropertiesManager::GetServerNetworkStats(
278     const net::HostPortPair& host_port_pair) const {
279   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
280   return http_server_properties_impl_->GetServerNetworkStats(host_port_pair);
281 }
282 
283 //
284 // Update the HttpServerPropertiesImpl's cache with data from preferences.
285 //
ScheduleUpdateCacheOnUI()286 void HttpServerPropertiesManager::ScheduleUpdateCacheOnUI() {
287   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
288   // Cancel pending updates, if any.
289   ui_cache_update_timer_->Stop();
290   StartCacheUpdateTimerOnUI(
291       base::TimeDelta::FromMilliseconds(kUpdateCacheDelayMs));
292 }
293 
StartCacheUpdateTimerOnUI(base::TimeDelta delay)294 void HttpServerPropertiesManager::StartCacheUpdateTimerOnUI(
295     base::TimeDelta delay) {
296   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
297   ui_cache_update_timer_->Start(
298       FROM_HERE, delay, this,
299       &HttpServerPropertiesManager::UpdateCacheFromPrefsOnUI);
300 }
301 
UpdateCacheFromPrefsOnUI()302 void HttpServerPropertiesManager::UpdateCacheFromPrefsOnUI() {
303   // The preferences can only be read on the UI thread.
304   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
305 
306   if (!pref_service_->HasPrefPath(prefs::kHttpServerProperties))
307     return;
308 
309   bool detected_corrupted_prefs = false;
310   const base::DictionaryValue& http_server_properties_dict =
311       *pref_service_->GetDictionary(prefs::kHttpServerProperties);
312 
313   int version = kMissingVersion;
314   if (!http_server_properties_dict.GetIntegerWithoutPathExpansion(
315       "version", &version)) {
316     DVLOG(1) << "Missing version. Clearing all properties.";
317     return;
318   }
319 
320   // The properties for a given server is in
321   // http_server_properties_dict["servers"][server].
322   const base::DictionaryValue* servers_dict = NULL;
323   if (!http_server_properties_dict.GetDictionaryWithoutPathExpansion(
324       "servers", &servers_dict)) {
325     DVLOG(1) << "Malformed http_server_properties for servers.";
326     return;
327   }
328 
329   // String is host/port pair of spdy server.
330   scoped_ptr<StringVector> spdy_servers(new StringVector);
331   scoped_ptr<net::SpdySettingsMap> spdy_settings_map(
332       new net::SpdySettingsMap(kMaxSpdySettingsHostsToPersist));
333   scoped_ptr<net::AlternateProtocolMap> alternate_protocol_map(
334       new net::AlternateProtocolMap(kMaxAlternateProtocolHostsToPersist));
335   // TODO(rtenneti): Delete the following code after the experiment.
336   int alternate_protocols_to_load = k200AlternateProtocolHostsToLoad;
337   net::AlternateProtocolExperiment alternate_protocol_experiment =
338       net::ALTERNATE_PROTOCOL_NOT_PART_OF_EXPERIMENT;
339   if (version == kVersionNumber) {
340     if (base::RandInt(0, 99) == 0) {
341       alternate_protocol_experiment =
342           net::ALTERNATE_PROTOCOL_TRUNCATED_200_SERVERS;
343     } else {
344       alternate_protocols_to_load = k1000AlternateProtocolHostsToLoad;
345       alternate_protocol_experiment =
346           net::ALTERNATE_PROTOCOL_TRUNCATED_1000_SERVERS;
347     }
348     DVLOG(1) << "# of servers that support alternate_protocol: "
349              << alternate_protocols_to_load;
350   }
351 
352   int count = 0;
353   for (base::DictionaryValue::Iterator it(*servers_dict); !it.IsAtEnd();
354        it.Advance()) {
355     // Get server's host/pair.
356     const std::string& server_str = it.key();
357     net::HostPortPair server = net::HostPortPair::FromString(server_str);
358     if (server.host().empty()) {
359       DVLOG(1) << "Malformed http_server_properties for server: " << server_str;
360       detected_corrupted_prefs = true;
361       continue;
362     }
363 
364     const base::DictionaryValue* server_pref_dict = NULL;
365     if (!it.value().GetAsDictionary(&server_pref_dict)) {
366       DVLOG(1) << "Malformed http_server_properties server: " << server_str;
367       detected_corrupted_prefs = true;
368       continue;
369     }
370 
371     // Get if server supports Spdy.
372     bool supports_spdy = false;
373     if ((server_pref_dict->GetBoolean(
374          "supports_spdy", &supports_spdy)) && supports_spdy) {
375       spdy_servers->push_back(server_str);
376     }
377 
378     // Get SpdySettings.
379     DCHECK(spdy_settings_map->Peek(server) == spdy_settings_map->end());
380     const base::DictionaryValue* spdy_settings_dict = NULL;
381     if (server_pref_dict->GetDictionaryWithoutPathExpansion(
382         "settings", &spdy_settings_dict)) {
383       net::SettingsMap settings_map;
384       for (base::DictionaryValue::Iterator dict_it(*spdy_settings_dict);
385            !dict_it.IsAtEnd(); dict_it.Advance()) {
386         const std::string& id_str = dict_it.key();
387         int id = 0;
388         if (!base::StringToInt(id_str, &id)) {
389           DVLOG(1) << "Malformed id in SpdySettings for server: " <<
390               server_str;
391           NOTREACHED();
392           continue;
393         }
394         int value = 0;
395         if (!dict_it.value().GetAsInteger(&value)) {
396           DVLOG(1) << "Malformed value in SpdySettings for server: " <<
397               server_str;
398           NOTREACHED();
399           continue;
400         }
401         net::SettingsFlagsAndValue flags_and_value(
402             net::SETTINGS_FLAG_PERSISTED, value);
403         settings_map[static_cast<net::SpdySettingsIds>(id)] = flags_and_value;
404       }
405       spdy_settings_map->Put(server, settings_map);
406     }
407 
408     // Get alternate_protocol server.
409     DCHECK(alternate_protocol_map->Peek(server) ==
410            alternate_protocol_map->end());
411     const base::DictionaryValue* port_alternate_protocol_dict = NULL;
412     if (!server_pref_dict->GetDictionaryWithoutPathExpansion(
413         "alternate_protocol", &port_alternate_protocol_dict)) {
414       continue;
415     }
416 
417     if (count >= alternate_protocols_to_load)
418       continue;
419     do {
420       int port = 0;
421       if (!port_alternate_protocol_dict->GetIntegerWithoutPathExpansion(
422           "port", &port) || (port > (1 << 16))) {
423         DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
424         detected_corrupted_prefs = true;
425         continue;
426       }
427       std::string protocol_str;
428       if (!port_alternate_protocol_dict->GetStringWithoutPathExpansion(
429               "protocol_str", &protocol_str)) {
430         DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
431         detected_corrupted_prefs = true;
432         continue;
433       }
434       net::AlternateProtocol protocol =
435           net::AlternateProtocolFromString(protocol_str);
436       if (!net::IsAlternateProtocolValid(protocol)) {
437         DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
438         detected_corrupted_prefs = true;
439         continue;
440       }
441 
442       net::PortAlternateProtocolPair port_alternate_protocol;
443       port_alternate_protocol.port = port;
444       port_alternate_protocol.protocol = protocol;
445 
446       alternate_protocol_map->Put(server, port_alternate_protocol);
447       ++count;
448     } while (false);
449   }
450 
451   BrowserThread::PostTask(
452       BrowserThread::IO,
453       FROM_HERE,
454       base::Bind(&HttpServerPropertiesManager::
455                  UpdateCacheFromPrefsOnIO,
456                  base::Unretained(this),
457                  base::Owned(spdy_servers.release()),
458                  base::Owned(spdy_settings_map.release()),
459                  base::Owned(alternate_protocol_map.release()),
460                  alternate_protocol_experiment,
461                  detected_corrupted_prefs));
462 }
463 
UpdateCacheFromPrefsOnIO(StringVector * spdy_servers,net::SpdySettingsMap * spdy_settings_map,net::AlternateProtocolMap * alternate_protocol_map,net::AlternateProtocolExperiment alternate_protocol_experiment,bool detected_corrupted_prefs)464 void HttpServerPropertiesManager::UpdateCacheFromPrefsOnIO(
465     StringVector* spdy_servers,
466     net::SpdySettingsMap* spdy_settings_map,
467     net::AlternateProtocolMap* alternate_protocol_map,
468     net::AlternateProtocolExperiment alternate_protocol_experiment,
469     bool detected_corrupted_prefs) {
470   // Preferences have the master data because admins might have pushed new
471   // preferences. Update the cached data with new data from preferences.
472   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
473 
474   UMA_HISTOGRAM_COUNTS("Net.CountOfSpdyServers", spdy_servers->size());
475   http_server_properties_impl_->InitializeSpdyServers(spdy_servers, true);
476 
477   // Update the cached data and use the new spdy_settings from preferences.
478   UMA_HISTOGRAM_COUNTS("Net.CountOfSpdySettings", spdy_settings_map->size());
479   http_server_properties_impl_->InitializeSpdySettingsServers(
480       spdy_settings_map);
481 
482   // Update the cached data and use the new Alternate-Protocol server list from
483   // preferences.
484   UMA_HISTOGRAM_COUNTS("Net.CountOfAlternateProtocolServers",
485                        alternate_protocol_map->size());
486   http_server_properties_impl_->InitializeAlternateProtocolServers(
487       alternate_protocol_map);
488   http_server_properties_impl_->SetAlternateProtocolExperiment(
489       alternate_protocol_experiment);
490 
491   // Update the prefs with what we have read (delete all corrupted prefs).
492   if (detected_corrupted_prefs)
493     ScheduleUpdatePrefsOnIO();
494 }
495 
496 
497 //
498 // Update Preferences with data from the cached data.
499 //
ScheduleUpdatePrefsOnIO()500 void HttpServerPropertiesManager::ScheduleUpdatePrefsOnIO() {
501   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
502   // Cancel pending updates, if any.
503   io_prefs_update_timer_->Stop();
504   StartPrefsUpdateTimerOnIO(
505       base::TimeDelta::FromMilliseconds(kUpdatePrefsDelayMs));
506 }
507 
StartPrefsUpdateTimerOnIO(base::TimeDelta delay)508 void HttpServerPropertiesManager::StartPrefsUpdateTimerOnIO(
509     base::TimeDelta delay) {
510   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
511   // This is overridden in tests to post the task without the delay.
512   io_prefs_update_timer_->Start(
513       FROM_HERE, delay, this,
514       &HttpServerPropertiesManager::UpdatePrefsFromCacheOnIO);
515 }
516 
517 // This is required so we can set this as the callback for a timer.
UpdatePrefsFromCacheOnIO()518 void HttpServerPropertiesManager::UpdatePrefsFromCacheOnIO() {
519   UpdatePrefsFromCacheOnIO(base::Closure());
520 }
521 
UpdatePrefsFromCacheOnIO(const base::Closure & completion)522 void HttpServerPropertiesManager::UpdatePrefsFromCacheOnIO(
523     const base::Closure& completion) {
524   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
525 
526   base::ListValue* spdy_server_list = new base::ListValue;
527   http_server_properties_impl_->GetSpdyServerList(
528       spdy_server_list, kMaxSupportsSpdyServerHostsToPersist);
529 
530   net::SpdySettingsMap* spdy_settings_map =
531       new net::SpdySettingsMap(kMaxSpdySettingsHostsToPersist);
532   const net::SpdySettingsMap& main_map =
533       http_server_properties_impl_->spdy_settings_map();
534   int count = 0;
535   for (net::SpdySettingsMap::const_iterator it = main_map.begin();
536        it != main_map.end() && count < kMaxSpdySettingsHostsToPersist;
537        ++it, ++count) {
538     spdy_settings_map->Put(it->first, it->second);
539   }
540 
541   net::AlternateProtocolMap* alternate_protocol_map =
542       new net::AlternateProtocolMap(kMaxAlternateProtocolHostsToPersist);
543   const net::AlternateProtocolMap& map =
544       http_server_properties_impl_->alternate_protocol_map();
545   count = 0;
546   typedef std::map<std::string, bool> CanonicalHostPersistedMap;
547   CanonicalHostPersistedMap persisted_map;
548   for (net::AlternateProtocolMap::const_iterator it = map.begin();
549        it != map.end() && count < kMaxAlternateProtocolHostsToPersist;
550        ++it) {
551     const net::HostPortPair& server = it->first;
552     std::string canonical_suffix =
553         http_server_properties_impl_->GetCanonicalSuffix(server);
554     if (!canonical_suffix.empty()) {
555       if (persisted_map.find(canonical_suffix) != persisted_map.end())
556         continue;
557       persisted_map[canonical_suffix] = true;
558     }
559     alternate_protocol_map->Put(server, it->second);
560     ++count;
561   }
562 
563   // Update the preferences on the UI thread.
564   BrowserThread::PostTask(
565       BrowserThread::UI,
566       FROM_HERE,
567       base::Bind(&HttpServerPropertiesManager::UpdatePrefsOnUI,
568                  ui_weak_ptr_,
569                  base::Owned(spdy_server_list),
570                  base::Owned(spdy_settings_map),
571                  base::Owned(alternate_protocol_map),
572                  completion));
573 }
574 
575 // A local or temporary data structure to hold |supports_spdy|, SpdySettings,
576 // and PortAlternateProtocolPair preferences for a server. This is used only in
577 // UpdatePrefsOnUI.
578 struct ServerPref {
ServerPrefchrome_browser_net::ServerPref579   ServerPref()
580       : supports_spdy(false),
581         settings_map(NULL),
582         alternate_protocol(NULL) {
583   }
ServerPrefchrome_browser_net::ServerPref584   ServerPref(bool supports_spdy,
585              const net::SettingsMap* settings_map,
586              const net::PortAlternateProtocolPair* alternate_protocol)
587       : supports_spdy(supports_spdy),
588         settings_map(settings_map),
589         alternate_protocol(alternate_protocol) {
590   }
591   bool supports_spdy;
592   const net::SettingsMap* settings_map;
593   const net::PortAlternateProtocolPair* alternate_protocol;
594 };
595 
UpdatePrefsOnUI(base::ListValue * spdy_server_list,net::SpdySettingsMap * spdy_settings_map,net::AlternateProtocolMap * alternate_protocol_map,const base::Closure & completion)596 void HttpServerPropertiesManager::UpdatePrefsOnUI(
597     base::ListValue* spdy_server_list,
598     net::SpdySettingsMap* spdy_settings_map,
599     net::AlternateProtocolMap* alternate_protocol_map,
600     const base::Closure& completion) {
601 
602   typedef std::map<net::HostPortPair, ServerPref> ServerPrefMap;
603   ServerPrefMap server_pref_map;
604 
605   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
606 
607   // Add servers that support spdy to server_pref_map.
608   std::string s;
609   for (base::ListValue::const_iterator list_it = spdy_server_list->begin();
610        list_it != spdy_server_list->end(); ++list_it) {
611     if ((*list_it)->GetAsString(&s)) {
612       net::HostPortPair server = net::HostPortPair::FromString(s);
613 
614       ServerPrefMap::iterator it = server_pref_map.find(server);
615       if (it == server_pref_map.end()) {
616         ServerPref server_pref(true, NULL, NULL);
617         server_pref_map[server] = server_pref;
618       } else {
619         it->second.supports_spdy = true;
620       }
621     }
622   }
623 
624   // Add servers that have SpdySettings to server_pref_map.
625   for (net::SpdySettingsMap::iterator map_it = spdy_settings_map->begin();
626        map_it != spdy_settings_map->end(); ++map_it) {
627     const net::HostPortPair& server = map_it->first;
628 
629     ServerPrefMap::iterator it = server_pref_map.find(server);
630     if (it == server_pref_map.end()) {
631       ServerPref server_pref(false, &map_it->second, NULL);
632       server_pref_map[server] = server_pref;
633     } else {
634       it->second.settings_map = &map_it->second;
635     }
636   }
637 
638   // Add AlternateProtocol servers to server_pref_map.
639   for (net::AlternateProtocolMap::const_iterator map_it =
640            alternate_protocol_map->begin();
641        map_it != alternate_protocol_map->end(); ++map_it) {
642     const net::HostPortPair& server = map_it->first;
643     const net::PortAlternateProtocolPair& port_alternate_protocol =
644         map_it->second;
645     if (!net::IsAlternateProtocolValid(port_alternate_protocol.protocol)) {
646       continue;
647     }
648 
649     ServerPrefMap::iterator it = server_pref_map.find(server);
650     if (it == server_pref_map.end()) {
651       ServerPref server_pref(false, NULL, &map_it->second);
652       server_pref_map[server] = server_pref;
653     } else {
654       it->second.alternate_protocol = &map_it->second;
655     }
656   }
657 
658   // Persist the prefs::kHttpServerProperties.
659   base::DictionaryValue http_server_properties_dict;
660   base::DictionaryValue* servers_dict = new base::DictionaryValue;
661   for (ServerPrefMap::const_iterator map_it =
662        server_pref_map.begin();
663        map_it != server_pref_map.end(); ++map_it) {
664     const net::HostPortPair& server = map_it->first;
665     const ServerPref& server_pref = map_it->second;
666 
667     base::DictionaryValue* server_pref_dict = new base::DictionaryValue;
668 
669     // Save supports_spdy.
670     if (server_pref.supports_spdy)
671       server_pref_dict->SetBoolean("supports_spdy", server_pref.supports_spdy);
672 
673     // Save SPDY settings.
674     if (server_pref.settings_map) {
675       base::DictionaryValue* spdy_settings_dict = new base::DictionaryValue;
676       for (net::SettingsMap::const_iterator it =
677            server_pref.settings_map->begin();
678            it != server_pref.settings_map->end(); ++it) {
679         net::SpdySettingsIds id = it->first;
680         uint32 value = it->second.second;
681         std::string key = base::StringPrintf("%u", id);
682         spdy_settings_dict->SetInteger(key, value);
683       }
684       server_pref_dict->SetWithoutPathExpansion("settings", spdy_settings_dict);
685     }
686 
687     // Save alternate_protocol.
688     if (server_pref.alternate_protocol) {
689       base::DictionaryValue* port_alternate_protocol_dict =
690           new base::DictionaryValue;
691       const net::PortAlternateProtocolPair* port_alternate_protocol =
692           server_pref.alternate_protocol;
693       port_alternate_protocol_dict->SetInteger(
694           "port", port_alternate_protocol->port);
695       const char* protocol_str =
696           net::AlternateProtocolToString(port_alternate_protocol->protocol);
697       port_alternate_protocol_dict->SetString("protocol_str", protocol_str);
698       server_pref_dict->SetWithoutPathExpansion(
699           "alternate_protocol", port_alternate_protocol_dict);
700     }
701 
702     servers_dict->SetWithoutPathExpansion(server.ToString(), server_pref_dict);
703   }
704 
705   http_server_properties_dict.SetWithoutPathExpansion("servers", servers_dict);
706   SetVersion(&http_server_properties_dict, kVersionNumber);
707   setting_prefs_ = true;
708   pref_service_->Set(prefs::kHttpServerProperties,
709                      http_server_properties_dict);
710   setting_prefs_ = false;
711 
712   // Note that |completion| will be fired after we have written everything to
713   // the Preferences, but likely before these changes are serialized to disk.
714   // This is not a problem though, as JSONPrefStore guarantees that this will
715   // happen, pretty soon, and even in the case we shut down immediately.
716   if (!completion.is_null())
717     completion.Run();
718 }
719 
OnHttpServerPropertiesChanged()720 void HttpServerPropertiesManager::OnHttpServerPropertiesChanged() {
721   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
722   if (!setting_prefs_)
723     ScheduleUpdateCacheOnUI();
724 }
725 
726 }  // namespace chrome_browser_net
727