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