Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / net / http / http_server_properties_manager.cc
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/http/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/single_thread_task_runner.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "base/values.h"
17
18 namespace net {
19
20 namespace {
21
22 // Time to wait before starting an update the http_server_properties_impl_ cache
23 // from preferences. Scheduling another update during this period will reset the
24 // timer.
25 const int64 kUpdateCacheDelayMs = 1000;
26
27 // Time to wait before starting an update the preferences from the
28 // http_server_properties_impl_ cache. Scheduling another update during this
29 // period will reset the timer.
30 const int64 kUpdatePrefsDelayMs = 5000;
31
32 // "version" 0 indicates, http_server_properties doesn't have "version"
33 // property.
34 const int kMissingVersion = 0;
35
36 // The version number of persisted http_server_properties.
37 const int kVersionNumber = 3;
38
39 typedef std::vector<std::string> StringVector;
40
41 // Load either 200 or 1000 servers based on a coin flip.
42 const int k200AlternateProtocolHostsToLoad = 200;
43 const int k1000AlternateProtocolHostsToLoad = 1000;
44 // Persist 1000 MRU AlternateProtocolHostPortPairs.
45 const int kMaxAlternateProtocolHostsToPersist = 1000;
46
47 // Persist 200 MRU SpdySettingsHostPortPairs.
48 const int kMaxSpdySettingsHostsToPersist = 200;
49
50 // Persist 300 MRU SupportsSpdyServerHostPortPairs.
51 const int kMaxSupportsSpdyServerHostsToPersist = 300;
52
53 }  // namespace
54
55 ////////////////////////////////////////////////////////////////////////////////
56 //  HttpServerPropertiesManager
57
58 HttpServerPropertiesManager::HttpServerPropertiesManager(
59     PrefService* pref_service,
60     const char* pref_path,
61     scoped_refptr<base::SequencedTaskRunner> network_task_runner)
62     : pref_task_runner_(base::ThreadTaskRunnerHandle::Get()),
63       pref_service_(pref_service),
64       setting_prefs_(false),
65       path_(pref_path),
66       network_task_runner_(network_task_runner) {
67   DCHECK(pref_service);
68   pref_weak_ptr_factory_.reset(
69       new base::WeakPtrFactory<HttpServerPropertiesManager>(this));
70   pref_weak_ptr_ = pref_weak_ptr_factory_->GetWeakPtr();
71   pref_cache_update_timer_.reset(
72       new base::OneShotTimer<HttpServerPropertiesManager>);
73   pref_change_registrar_.Init(pref_service_);
74   pref_change_registrar_.Add(
75       path_,
76       base::Bind(&HttpServerPropertiesManager::OnHttpServerPropertiesChanged,
77                  base::Unretained(this)));
78 }
79
80 HttpServerPropertiesManager::~HttpServerPropertiesManager() {
81   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
82   network_weak_ptr_factory_.reset();
83 }
84
85 void HttpServerPropertiesManager::InitializeOnNetworkThread() {
86   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
87   network_weak_ptr_factory_.reset(
88       new base::WeakPtrFactory<HttpServerPropertiesManager>(this));
89   http_server_properties_impl_.reset(new net::HttpServerPropertiesImpl());
90
91   network_prefs_update_timer_.reset(
92       new base::OneShotTimer<HttpServerPropertiesManager>);
93
94   pref_task_runner_->PostTask(
95       FROM_HERE,
96       base::Bind(&HttpServerPropertiesManager::UpdateCacheFromPrefsOnPrefThread,
97                  pref_weak_ptr_));
98 }
99
100 void HttpServerPropertiesManager::ShutdownOnPrefThread() {
101   DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
102   // Cancel any pending updates, and stop listening for pref change updates.
103   pref_cache_update_timer_->Stop();
104   pref_weak_ptr_factory_.reset();
105   pref_change_registrar_.RemoveAll();
106 }
107
108 // static
109 void HttpServerPropertiesManager::SetVersion(
110     base::DictionaryValue* http_server_properties_dict,
111     int version_number) {
112   if (version_number < 0)
113     version_number = kVersionNumber;
114   DCHECK_LE(version_number, kVersionNumber);
115   if (version_number <= kVersionNumber)
116     http_server_properties_dict->SetInteger("version", version_number);
117 }
118
119 // This is required for conformance with the HttpServerProperties interface.
120 base::WeakPtr<net::HttpServerProperties>
121 HttpServerPropertiesManager::GetWeakPtr() {
122   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
123   return network_weak_ptr_factory_->GetWeakPtr();
124 }
125
126 void HttpServerPropertiesManager::Clear() {
127   Clear(base::Closure());
128 }
129
130 void HttpServerPropertiesManager::Clear(const base::Closure& completion) {
131   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
132
133   http_server_properties_impl_->Clear();
134   UpdatePrefsFromCacheOnNetworkThread(completion);
135 }
136
137 bool HttpServerPropertiesManager::SupportsSpdy(
138     const net::HostPortPair& server) {
139   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
140   return http_server_properties_impl_->SupportsSpdy(server);
141 }
142
143 void HttpServerPropertiesManager::SetSupportsSpdy(
144     const net::HostPortPair& server,
145     bool support_spdy) {
146   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
147
148   http_server_properties_impl_->SetSupportsSpdy(server, support_spdy);
149   ScheduleUpdatePrefsOnNetworkThread();
150 }
151
152 bool HttpServerPropertiesManager::HasAlternateProtocol(
153     const net::HostPortPair& server) {
154   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
155   return http_server_properties_impl_->HasAlternateProtocol(server);
156 }
157
158 net::AlternateProtocolInfo
159 HttpServerPropertiesManager::GetAlternateProtocol(
160     const net::HostPortPair& server) {
161   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
162   return http_server_properties_impl_->GetAlternateProtocol(server);
163 }
164
165 void HttpServerPropertiesManager::SetAlternateProtocol(
166     const net::HostPortPair& server,
167     uint16 alternate_port,
168     AlternateProtocol alternate_protocol,
169     double alternate_probability) {
170   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
171   http_server_properties_impl_->SetAlternateProtocol(
172       server, alternate_port, alternate_protocol, alternate_probability);
173   ScheduleUpdatePrefsOnNetworkThread();
174 }
175
176 void HttpServerPropertiesManager::SetBrokenAlternateProtocol(
177     const net::HostPortPair& server) {
178   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
179   http_server_properties_impl_->SetBrokenAlternateProtocol(server);
180   ScheduleUpdatePrefsOnNetworkThread();
181 }
182
183 bool HttpServerPropertiesManager::WasAlternateProtocolRecentlyBroken(
184     const net::HostPortPair& server) {
185   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
186   return http_server_properties_impl_->WasAlternateProtocolRecentlyBroken(
187       server);
188 }
189
190 void HttpServerPropertiesManager::ConfirmAlternateProtocol(
191     const net::HostPortPair& server) {
192   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
193   http_server_properties_impl_->ConfirmAlternateProtocol(server);
194   ScheduleUpdatePrefsOnNetworkThread();
195 }
196
197 void HttpServerPropertiesManager::ClearAlternateProtocol(
198     const net::HostPortPair& server) {
199   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
200   http_server_properties_impl_->ClearAlternateProtocol(server);
201   ScheduleUpdatePrefsOnNetworkThread();
202 }
203
204 const net::AlternateProtocolMap&
205 HttpServerPropertiesManager::alternate_protocol_map() const {
206   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
207   return http_server_properties_impl_->alternate_protocol_map();
208 }
209
210 void HttpServerPropertiesManager::SetAlternateProtocolExperiment(
211     AlternateProtocolExperiment experiment) {
212   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
213   http_server_properties_impl_->SetAlternateProtocolExperiment(experiment);
214 }
215
216 void HttpServerPropertiesManager::SetAlternateProtocolProbabilityThreshold(
217     double threshold) {
218   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
219   http_server_properties_impl_->SetAlternateProtocolProbabilityThreshold(
220       threshold);
221 }
222
223 AlternateProtocolExperiment
224 HttpServerPropertiesManager::GetAlternateProtocolExperiment() const {
225   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
226   return http_server_properties_impl_->GetAlternateProtocolExperiment();
227 }
228
229 const SettingsMap& HttpServerPropertiesManager::GetSpdySettings(
230     const HostPortPair& host_port_pair) {
231   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
232   return http_server_properties_impl_->GetSpdySettings(host_port_pair);
233 }
234
235 bool HttpServerPropertiesManager::SetSpdySetting(
236     const HostPortPair& host_port_pair,
237     SpdySettingsIds id,
238     SpdySettingsFlags flags,
239     uint32 value) {
240   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
241   bool persist = http_server_properties_impl_->SetSpdySetting(
242       host_port_pair, id, flags, value);
243   if (persist)
244     ScheduleUpdatePrefsOnNetworkThread();
245   return persist;
246 }
247
248 void HttpServerPropertiesManager::ClearSpdySettings(
249     const HostPortPair& host_port_pair) {
250   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
251   http_server_properties_impl_->ClearSpdySettings(host_port_pair);
252   ScheduleUpdatePrefsOnNetworkThread();
253 }
254
255 void HttpServerPropertiesManager::ClearAllSpdySettings() {
256   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
257   http_server_properties_impl_->ClearAllSpdySettings();
258   ScheduleUpdatePrefsOnNetworkThread();
259 }
260
261 const SpdySettingsMap& HttpServerPropertiesManager::spdy_settings_map()
262     const {
263   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
264   return http_server_properties_impl_->spdy_settings_map();
265 }
266
267 void HttpServerPropertiesManager::SetServerNetworkStats(
268     const net::HostPortPair& host_port_pair,
269     NetworkStats stats) {
270   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
271   http_server_properties_impl_->SetServerNetworkStats(host_port_pair, stats);
272 }
273
274 const HttpServerPropertiesManager::NetworkStats*
275 HttpServerPropertiesManager::GetServerNetworkStats(
276     const net::HostPortPair& host_port_pair) const {
277   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
278   return http_server_properties_impl_->GetServerNetworkStats(host_port_pair);
279 }
280
281 //
282 // Update the HttpServerPropertiesImpl's cache with data from preferences.
283 //
284 void HttpServerPropertiesManager::ScheduleUpdateCacheOnPrefThread() {
285   DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
286   // Cancel pending updates, if any.
287   pref_cache_update_timer_->Stop();
288   StartCacheUpdateTimerOnPrefThread(
289       base::TimeDelta::FromMilliseconds(kUpdateCacheDelayMs));
290 }
291
292 void HttpServerPropertiesManager::StartCacheUpdateTimerOnPrefThread(
293     base::TimeDelta delay) {
294   DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
295   pref_cache_update_timer_->Start(
296       FROM_HERE,
297       delay,
298       this,
299       &HttpServerPropertiesManager::UpdateCacheFromPrefsOnPrefThread);
300 }
301
302 void HttpServerPropertiesManager::UpdateCacheFromPrefsOnPrefThread() {
303   // The preferences can only be read on the pref thread.
304   DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
305
306   if (!pref_service_->HasPrefPath(path_))
307     return;
308
309   bool detected_corrupted_prefs = false;
310   const base::DictionaryValue& http_server_properties_dict =
311       *pref_service_->GetDictionary(path_);
312
313   int version = kMissingVersion;
314   if (!http_server_properties_dict.GetIntegerWithoutPathExpansion("version",
315                                                                   &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("supports_spdy", &supports_spdy)) &&
374         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();
386            dict_it.Advance()) {
387         const std::string& id_str = dict_it.key();
388         int id = 0;
389         if (!base::StringToInt(id_str, &id)) {
390           DVLOG(1) << "Malformed id in SpdySettings for server: " << 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(net::SETTINGS_FLAG_PERSISTED,
402                                                    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) ||
423           (port > (1 << 16))) {
424         DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
425         detected_corrupted_prefs = true;
426         continue;
427       }
428       std::string protocol_str;
429       if (!port_alternate_protocol_dict->GetStringWithoutPathExpansion(
430               "protocol_str", &protocol_str)) {
431         DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
432         detected_corrupted_prefs = true;
433         continue;
434       }
435       net::AlternateProtocol protocol =
436           net::AlternateProtocolFromString(protocol_str);
437       if (!net::IsAlternateProtocolValid(protocol)) {
438         DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
439         detected_corrupted_prefs = true;
440         continue;
441       }
442
443       double probability = 1;
444       if (port_alternate_protocol_dict->HasKey("probability") &&
445           !port_alternate_protocol_dict->GetDoubleWithoutPathExpansion(
446               "probability", &probability)) {
447         DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
448         detected_corrupted_prefs = true;
449         continue;
450       }
451
452       net::AlternateProtocolInfo port_alternate_protocol(port,
453                                                          protocol,
454                                                          probability);
455       alternate_protocol_map->Put(server, port_alternate_protocol);
456       ++count;
457     } while (false);
458   }
459
460   network_task_runner_->PostTask(
461       FROM_HERE,
462       base::Bind(
463           &HttpServerPropertiesManager::UpdateCacheFromPrefsOnNetworkThread,
464           base::Unretained(this),
465           base::Owned(spdy_servers.release()),
466           base::Owned(spdy_settings_map.release()),
467           base::Owned(alternate_protocol_map.release()),
468           alternate_protocol_experiment,
469           detected_corrupted_prefs));
470 }
471
472 void HttpServerPropertiesManager::UpdateCacheFromPrefsOnNetworkThread(
473     StringVector* spdy_servers,
474     net::SpdySettingsMap* spdy_settings_map,
475     net::AlternateProtocolMap* alternate_protocol_map,
476     net::AlternateProtocolExperiment alternate_protocol_experiment,
477     bool detected_corrupted_prefs) {
478   // Preferences have the master data because admins might have pushed new
479   // preferences. Update the cached data with new data from preferences.
480   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
481
482   UMA_HISTOGRAM_COUNTS("Net.CountOfSpdyServers", spdy_servers->size());
483   http_server_properties_impl_->InitializeSpdyServers(spdy_servers, true);
484
485   // Update the cached data and use the new spdy_settings from preferences.
486   UMA_HISTOGRAM_COUNTS("Net.CountOfSpdySettings", spdy_settings_map->size());
487   http_server_properties_impl_->InitializeSpdySettingsServers(
488       spdy_settings_map);
489
490   // Update the cached data and use the new Alternate-Protocol server list from
491   // preferences.
492   UMA_HISTOGRAM_COUNTS("Net.CountOfAlternateProtocolServers",
493                        alternate_protocol_map->size());
494   http_server_properties_impl_->InitializeAlternateProtocolServers(
495       alternate_protocol_map);
496   http_server_properties_impl_->SetAlternateProtocolExperiment(
497       alternate_protocol_experiment);
498
499   // Update the prefs with what we have read (delete all corrupted prefs).
500   if (detected_corrupted_prefs)
501     ScheduleUpdatePrefsOnNetworkThread();
502 }
503
504 //
505 // Update Preferences with data from the cached data.
506 //
507 void HttpServerPropertiesManager::ScheduleUpdatePrefsOnNetworkThread() {
508   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
509   // Cancel pending updates, if any.
510   network_prefs_update_timer_->Stop();
511   StartPrefsUpdateTimerOnNetworkThread(
512       base::TimeDelta::FromMilliseconds(kUpdatePrefsDelayMs));
513 }
514
515 void HttpServerPropertiesManager::StartPrefsUpdateTimerOnNetworkThread(
516     base::TimeDelta delay) {
517   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
518   // This is overridden in tests to post the task without the delay.
519   network_prefs_update_timer_->Start(
520       FROM_HERE,
521       delay,
522       this,
523       &HttpServerPropertiesManager::UpdatePrefsFromCacheOnNetworkThread);
524 }
525
526 // This is required so we can set this as the callback for a timer.
527 void HttpServerPropertiesManager::UpdatePrefsFromCacheOnNetworkThread() {
528   UpdatePrefsFromCacheOnNetworkThread(base::Closure());
529 }
530
531 void HttpServerPropertiesManager::UpdatePrefsFromCacheOnNetworkThread(
532     const base::Closure& completion) {
533   DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
534
535   base::ListValue* spdy_server_list = new base::ListValue;
536   http_server_properties_impl_->GetSpdyServerList(
537       spdy_server_list, kMaxSupportsSpdyServerHostsToPersist);
538
539   net::SpdySettingsMap* spdy_settings_map =
540       new net::SpdySettingsMap(kMaxSpdySettingsHostsToPersist);
541   const net::SpdySettingsMap& main_map =
542       http_server_properties_impl_->spdy_settings_map();
543   int count = 0;
544   for (net::SpdySettingsMap::const_iterator it = main_map.begin();
545        it != main_map.end() && count < kMaxSpdySettingsHostsToPersist;
546        ++it, ++count) {
547     spdy_settings_map->Put(it->first, it->second);
548   }
549
550   net::AlternateProtocolMap* alternate_protocol_map =
551       new net::AlternateProtocolMap(kMaxAlternateProtocolHostsToPersist);
552   const net::AlternateProtocolMap& map =
553       http_server_properties_impl_->alternate_protocol_map();
554   count = 0;
555   typedef std::map<std::string, bool> CanonicalHostPersistedMap;
556   CanonicalHostPersistedMap persisted_map;
557   for (net::AlternateProtocolMap::const_iterator it = map.begin();
558        it != map.end() && count < kMaxAlternateProtocolHostsToPersist;
559        ++it) {
560     const net::HostPortPair& server = it->first;
561     std::string canonical_suffix =
562         http_server_properties_impl_->GetCanonicalSuffix(server);
563     if (!canonical_suffix.empty()) {
564       if (persisted_map.find(canonical_suffix) != persisted_map.end())
565         continue;
566       persisted_map[canonical_suffix] = true;
567     }
568     alternate_protocol_map->Put(server, it->second);
569     ++count;
570   }
571
572   // Update the preferences on the pref thread.
573   pref_task_runner_->PostTask(
574       FROM_HERE,
575       base::Bind(&HttpServerPropertiesManager::UpdatePrefsOnPrefThread,
576                  pref_weak_ptr_,
577                  base::Owned(spdy_server_list),
578                  base::Owned(spdy_settings_map),
579                  base::Owned(alternate_protocol_map),
580                  completion));
581 }
582
583 // A local or temporary data structure to hold |supports_spdy|, SpdySettings,
584 // and AlternateProtocolInfo preferences for a server. This is used only in
585 // UpdatePrefsOnPrefThread.
586 struct ServerPref {
587   ServerPref()
588       : supports_spdy(false), settings_map(NULL), alternate_protocol(NULL) {}
589   ServerPref(bool supports_spdy,
590              const net::SettingsMap* settings_map,
591              const net::AlternateProtocolInfo* alternate_protocol)
592       : supports_spdy(supports_spdy),
593         settings_map(settings_map),
594         alternate_protocol(alternate_protocol) {}
595   bool supports_spdy;
596   const net::SettingsMap* settings_map;
597   const net::AlternateProtocolInfo* alternate_protocol;
598 };
599
600 void HttpServerPropertiesManager::UpdatePrefsOnPrefThread(
601     base::ListValue* spdy_server_list,
602     net::SpdySettingsMap* spdy_settings_map,
603     net::AlternateProtocolMap* alternate_protocol_map,
604     const base::Closure& completion) {
605   typedef std::map<net::HostPortPair, ServerPref> ServerPrefMap;
606   ServerPrefMap server_pref_map;
607
608   DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
609
610   // Add servers that support spdy to server_pref_map.
611   std::string s;
612   for (base::ListValue::const_iterator list_it = spdy_server_list->begin();
613        list_it != spdy_server_list->end();
614        ++list_it) {
615     if ((*list_it)->GetAsString(&s)) {
616       net::HostPortPair server = net::HostPortPair::FromString(s);
617
618       ServerPrefMap::iterator it = server_pref_map.find(server);
619       if (it == server_pref_map.end()) {
620         ServerPref server_pref(true, NULL, NULL);
621         server_pref_map[server] = server_pref;
622       } else {
623         it->second.supports_spdy = true;
624       }
625     }
626   }
627
628   // Add servers that have SpdySettings to server_pref_map.
629   for (net::SpdySettingsMap::iterator map_it = spdy_settings_map->begin();
630        map_it != spdy_settings_map->end();
631        ++map_it) {
632     const net::HostPortPair& server = map_it->first;
633
634     ServerPrefMap::iterator it = server_pref_map.find(server);
635     if (it == server_pref_map.end()) {
636       ServerPref server_pref(false, &map_it->second, NULL);
637       server_pref_map[server] = server_pref;
638     } else {
639       it->second.settings_map = &map_it->second;
640     }
641   }
642
643   // Add AlternateProtocol servers to server_pref_map.
644   for (net::AlternateProtocolMap::const_iterator map_it =
645            alternate_protocol_map->begin();
646        map_it != alternate_protocol_map->end();
647        ++map_it) {
648     const net::HostPortPair& server = map_it->first;
649     const net::AlternateProtocolInfo& port_alternate_protocol =
650         map_it->second;
651     if (!net::IsAlternateProtocolValid(port_alternate_protocol.protocol)) {
652       continue;
653     }
654
655     ServerPrefMap::iterator it = server_pref_map.find(server);
656     if (it == server_pref_map.end()) {
657       ServerPref server_pref(false, NULL, &map_it->second);
658       server_pref_map[server] = server_pref;
659     } else {
660       it->second.alternate_protocol = &map_it->second;
661     }
662   }
663
664   // Persist properties to the |path_|.
665   base::DictionaryValue http_server_properties_dict;
666   base::DictionaryValue* servers_dict = new base::DictionaryValue;
667   for (ServerPrefMap::const_iterator map_it = server_pref_map.begin();
668        map_it != server_pref_map.end();
669        ++map_it) {
670     const net::HostPortPair& server = map_it->first;
671     const ServerPref& server_pref = map_it->second;
672
673     base::DictionaryValue* server_pref_dict = new base::DictionaryValue;
674
675     // Save supports_spdy.
676     if (server_pref.supports_spdy)
677       server_pref_dict->SetBoolean("supports_spdy", server_pref.supports_spdy);
678
679     // Save SPDY settings.
680     if (server_pref.settings_map) {
681       base::DictionaryValue* spdy_settings_dict = new base::DictionaryValue;
682       for (net::SettingsMap::const_iterator it =
683                server_pref.settings_map->begin();
684            it != server_pref.settings_map->end();
685            ++it) {
686         net::SpdySettingsIds id = it->first;
687         uint32 value = it->second.second;
688         std::string key = base::StringPrintf("%u", id);
689         spdy_settings_dict->SetInteger(key, value);
690       }
691       server_pref_dict->SetWithoutPathExpansion("settings", spdy_settings_dict);
692     }
693
694     // Save alternate_protocol.
695     if (server_pref.alternate_protocol) {
696       base::DictionaryValue* port_alternate_protocol_dict =
697           new base::DictionaryValue;
698       const net::AlternateProtocolInfo* port_alternate_protocol =
699           server_pref.alternate_protocol;
700       port_alternate_protocol_dict->SetInteger("port",
701                                                port_alternate_protocol->port);
702       const char* protocol_str =
703           net::AlternateProtocolToString(port_alternate_protocol->protocol);
704       port_alternate_protocol_dict->SetString("protocol_str", protocol_str);
705       port_alternate_protocol_dict->SetDouble(
706           "probability", port_alternate_protocol->probability);
707       server_pref_dict->SetWithoutPathExpansion(
708           "alternate_protocol", port_alternate_protocol_dict);
709     }
710
711     servers_dict->SetWithoutPathExpansion(server.ToString(), server_pref_dict);
712   }
713
714   http_server_properties_dict.SetWithoutPathExpansion("servers", servers_dict);
715   SetVersion(&http_server_properties_dict, kVersionNumber);
716   setting_prefs_ = true;
717   pref_service_->Set(path_, http_server_properties_dict);
718   setting_prefs_ = false;
719
720   // Note that |completion| will be fired after we have written everything to
721   // the Preferences, but likely before these changes are serialized to disk.
722   // This is not a problem though, as JSONPrefStore guarantees that this will
723   // happen, pretty soon, and even in the case we shut down immediately.
724   if (!completion.is_null())
725     completion.Run();
726 }
727
728 void HttpServerPropertiesManager::OnHttpServerPropertiesChanged() {
729   DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
730   if (!setting_prefs_)
731     ScheduleUpdateCacheOnPrefThread();
732 }
733
734 }  // namespace net