Home | History | Annotate | Download | only in net
      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 
     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 
     82 HttpServerPropertiesManager::~HttpServerPropertiesManager() {
     83   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     84   io_weak_ptr_factory_.reset();
     85 }
     86 
     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 
    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
    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
    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>
    132     HttpServerPropertiesManager::GetWeakPtr() {
    133   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    134   return io_weak_ptr_factory_->GetWeakPtr();
    135 }
    136 
    137 void HttpServerPropertiesManager::Clear() {
    138   Clear(base::Closure());
    139 }
    140 
    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 
    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 
    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 
    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
    170 HttpServerPropertiesManager::GetAlternateProtocol(
    171     const net::HostPortPair& server) {
    172   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    173   return http_server_properties_impl_->GetAlternateProtocol(server);
    174 }
    175 
    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 
    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 
    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 
    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 
    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&
    215 HttpServerPropertiesManager::alternate_protocol_map() const {
    216   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    217   return http_server_properties_impl_->alternate_protocol_map();
    218 }
    219 
    220 void HttpServerPropertiesManager::SetAlternateProtocolExperiment(
    221     net::AlternateProtocolExperiment experiment) {
    222   http_server_properties_impl_->SetAlternateProtocolExperiment(experiment);
    223 }
    224 
    225 net::AlternateProtocolExperiment
    226 HttpServerPropertiesManager::GetAlternateProtocolExperiment() const {
    227   return http_server_properties_impl_->GetAlternateProtocolExperiment();
    228 }
    229 
    230 const net::SettingsMap&
    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 
    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 
    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 
    257 void HttpServerPropertiesManager::ClearAllSpdySettings() {
    258   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    259   http_server_properties_impl_->ClearAllSpdySettings();
    260   ScheduleUpdatePrefsOnIO();
    261 }
    262 
    263 const net::SpdySettingsMap&
    264 HttpServerPropertiesManager::spdy_settings_map() const {
    265   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    266   return http_server_properties_impl_->spdy_settings_map();
    267 }
    268 
    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*
    277 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 //
    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 
    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 
    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 
    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 //
    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 
    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.
    518 void HttpServerPropertiesManager::UpdatePrefsFromCacheOnIO() {
    519   UpdatePrefsFromCacheOnIO(base::Closure());
    520 }
    521 
    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 {
    579   ServerPref()
    580       : supports_spdy(false),
    581         settings_map(NULL),
    582         alternate_protocol(NULL) {
    583   }
    584   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 
    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 
    720 void HttpServerPropertiesManager::OnHttpServerPropertiesChanged() {
    721   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    722   if (!setting_prefs_)
    723     ScheduleUpdateCacheOnUI();
    724 }
    725 
    726 }  // namespace chrome_browser_net
    727