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
18namespace net {
19
20namespace {
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.
25const 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.
30const int64 kUpdatePrefsDelayMs = 5000;
31
32// "version" 0 indicates, http_server_properties doesn't have "version"
33// property.
34const int kMissingVersion = 0;
35
36// The version number of persisted http_server_properties.
37const int kVersionNumber = 3;
38
39typedef std::vector<std::string> StringVector;
40
41// Load either 200 or 1000 servers based on a coin flip.
42const int k200AlternateProtocolHostsToLoad = 200;
43const int k1000AlternateProtocolHostsToLoad = 1000;
44// Persist 1000 MRU AlternateProtocolHostPortPairs.
45const int kMaxAlternateProtocolHostsToPersist = 1000;
46
47// Persist 200 MRU SpdySettingsHostPortPairs.
48const int kMaxSpdySettingsHostsToPersist = 200;
49
50// Persist 300 MRU SupportsSpdyServerHostPortPairs.
51const int kMaxSupportsSpdyServerHostsToPersist = 300;
52
53}  // namespace
54
55////////////////////////////////////////////////////////////////////////////////
56//  HttpServerPropertiesManager
57
58HttpServerPropertiesManager::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
80HttpServerPropertiesManager::~HttpServerPropertiesManager() {
81  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
82  network_weak_ptr_factory_.reset();
83}
84
85void 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
100void 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
109void 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.
120base::WeakPtr<net::HttpServerProperties>
121HttpServerPropertiesManager::GetWeakPtr() {
122  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
123  return network_weak_ptr_factory_->GetWeakPtr();
124}
125
126void HttpServerPropertiesManager::Clear() {
127  Clear(base::Closure());
128}
129
130void HttpServerPropertiesManager::Clear(const base::Closure& completion) {
131  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
132
133  http_server_properties_impl_->Clear();
134  UpdatePrefsFromCacheOnNetworkThread(completion);
135}
136
137bool HttpServerPropertiesManager::SupportsSpdy(
138    const net::HostPortPair& server) {
139  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
140  return http_server_properties_impl_->SupportsSpdy(server);
141}
142
143void 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
152bool HttpServerPropertiesManager::HasAlternateProtocol(
153    const net::HostPortPair& server) {
154  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
155  return http_server_properties_impl_->HasAlternateProtocol(server);
156}
157
158net::AlternateProtocolInfo
159HttpServerPropertiesManager::GetAlternateProtocol(
160    const net::HostPortPair& server) {
161  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
162  return http_server_properties_impl_->GetAlternateProtocol(server);
163}
164
165void 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
176void HttpServerPropertiesManager::SetBrokenAlternateProtocol(
177    const net::HostPortPair& server) {
178  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
179  http_server_properties_impl_->SetBrokenAlternateProtocol(server);
180  ScheduleUpdatePrefsOnNetworkThread();
181}
182
183bool HttpServerPropertiesManager::WasAlternateProtocolRecentlyBroken(
184    const net::HostPortPair& server) {
185  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
186  return http_server_properties_impl_->WasAlternateProtocolRecentlyBroken(
187      server);
188}
189
190void HttpServerPropertiesManager::ConfirmAlternateProtocol(
191    const net::HostPortPair& server) {
192  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
193  http_server_properties_impl_->ConfirmAlternateProtocol(server);
194  ScheduleUpdatePrefsOnNetworkThread();
195}
196
197void HttpServerPropertiesManager::ClearAlternateProtocol(
198    const net::HostPortPair& server) {
199  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
200  http_server_properties_impl_->ClearAlternateProtocol(server);
201  ScheduleUpdatePrefsOnNetworkThread();
202}
203
204const net::AlternateProtocolMap&
205HttpServerPropertiesManager::alternate_protocol_map() const {
206  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
207  return http_server_properties_impl_->alternate_protocol_map();
208}
209
210void HttpServerPropertiesManager::SetAlternateProtocolExperiment(
211    AlternateProtocolExperiment experiment) {
212  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
213  http_server_properties_impl_->SetAlternateProtocolExperiment(experiment);
214}
215
216void HttpServerPropertiesManager::SetAlternateProtocolProbabilityThreshold(
217    double threshold) {
218  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
219  http_server_properties_impl_->SetAlternateProtocolProbabilityThreshold(
220      threshold);
221}
222
223AlternateProtocolExperiment
224HttpServerPropertiesManager::GetAlternateProtocolExperiment() const {
225  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
226  return http_server_properties_impl_->GetAlternateProtocolExperiment();
227}
228
229const 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
235bool 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
248void 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
255void HttpServerPropertiesManager::ClearAllSpdySettings() {
256  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
257  http_server_properties_impl_->ClearAllSpdySettings();
258  ScheduleUpdatePrefsOnNetworkThread();
259}
260
261const SpdySettingsMap& HttpServerPropertiesManager::spdy_settings_map()
262    const {
263  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
264  return http_server_properties_impl_->spdy_settings_map();
265}
266
267net::SupportsQuic
268HttpServerPropertiesManager::GetSupportsQuic(
269    const net::HostPortPair& host_port_pair) const {
270  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
271  return http_server_properties_impl_->GetSupportsQuic(host_port_pair);
272}
273
274void HttpServerPropertiesManager::SetSupportsQuic(
275    const net::HostPortPair& host_port_pair,
276    bool used_quic,
277    const std::string& address) {
278  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
279  http_server_properties_impl_->SetSupportsQuic(
280      host_port_pair, used_quic, address);
281  ScheduleUpdatePrefsOnNetworkThread();
282}
283
284const SupportsQuicMap& HttpServerPropertiesManager::supports_quic_map()
285    const {
286  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
287  return http_server_properties_impl_->supports_quic_map();
288}
289
290void HttpServerPropertiesManager::SetServerNetworkStats(
291    const net::HostPortPair& host_port_pair,
292    NetworkStats stats) {
293  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
294  http_server_properties_impl_->SetServerNetworkStats(host_port_pair, stats);
295}
296
297const HttpServerPropertiesManager::NetworkStats*
298HttpServerPropertiesManager::GetServerNetworkStats(
299    const net::HostPortPair& host_port_pair) const {
300  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
301  return http_server_properties_impl_->GetServerNetworkStats(host_port_pair);
302}
303
304//
305// Update the HttpServerPropertiesImpl's cache with data from preferences.
306//
307void HttpServerPropertiesManager::ScheduleUpdateCacheOnPrefThread() {
308  DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
309  // Cancel pending updates, if any.
310  pref_cache_update_timer_->Stop();
311  StartCacheUpdateTimerOnPrefThread(
312      base::TimeDelta::FromMilliseconds(kUpdateCacheDelayMs));
313}
314
315void HttpServerPropertiesManager::StartCacheUpdateTimerOnPrefThread(
316    base::TimeDelta delay) {
317  DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
318  pref_cache_update_timer_->Start(
319      FROM_HERE,
320      delay,
321      this,
322      &HttpServerPropertiesManager::UpdateCacheFromPrefsOnPrefThread);
323}
324
325void HttpServerPropertiesManager::UpdateCacheFromPrefsOnPrefThread() {
326  // The preferences can only be read on the pref thread.
327  DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
328
329  if (!pref_service_->HasPrefPath(path_))
330    return;
331
332  bool detected_corrupted_prefs = false;
333  const base::DictionaryValue& http_server_properties_dict =
334      *pref_service_->GetDictionary(path_);
335
336  int version = kMissingVersion;
337  if (!http_server_properties_dict.GetIntegerWithoutPathExpansion("version",
338                                                                  &version)) {
339    DVLOG(1) << "Missing version. Clearing all properties.";
340    return;
341  }
342
343  // The properties for a given server is in
344  // http_server_properties_dict["servers"][server].
345  const base::DictionaryValue* servers_dict = NULL;
346  if (!http_server_properties_dict.GetDictionaryWithoutPathExpansion(
347          "servers", &servers_dict)) {
348    DVLOG(1) << "Malformed http_server_properties for servers.";
349    return;
350  }
351
352  // String is host/port pair of spdy server.
353  scoped_ptr<StringVector> spdy_servers(new StringVector);
354  scoped_ptr<net::SpdySettingsMap> spdy_settings_map(
355      new net::SpdySettingsMap(kMaxSpdySettingsHostsToPersist));
356  scoped_ptr<net::AlternateProtocolMap> alternate_protocol_map(
357      new net::AlternateProtocolMap(kMaxAlternateProtocolHostsToPersist));
358  scoped_ptr<net::SupportsQuicMap> supports_quic_map(
359      new net::SupportsQuicMap());
360  // TODO(rtenneti): Delete the following code after the experiment.
361  int alternate_protocols_to_load = k200AlternateProtocolHostsToLoad;
362  net::AlternateProtocolExperiment alternate_protocol_experiment =
363      net::ALTERNATE_PROTOCOL_NOT_PART_OF_EXPERIMENT;
364  if (version == kVersionNumber) {
365    if (base::RandInt(0, 99) == 0) {
366      alternate_protocol_experiment =
367          net::ALTERNATE_PROTOCOL_TRUNCATED_200_SERVERS;
368    } else {
369      alternate_protocols_to_load = k1000AlternateProtocolHostsToLoad;
370      alternate_protocol_experiment =
371          net::ALTERNATE_PROTOCOL_TRUNCATED_1000_SERVERS;
372    }
373    DVLOG(1) << "# of servers that support alternate_protocol: "
374             << alternate_protocols_to_load;
375  }
376
377  int count = 0;
378  for (base::DictionaryValue::Iterator it(*servers_dict); !it.IsAtEnd();
379       it.Advance()) {
380    // Get server's host/pair.
381    const std::string& server_str = it.key();
382    net::HostPortPair server = net::HostPortPair::FromString(server_str);
383    if (server.host().empty()) {
384      DVLOG(1) << "Malformed http_server_properties for server: " << server_str;
385      detected_corrupted_prefs = true;
386      continue;
387    }
388
389    const base::DictionaryValue* server_pref_dict = NULL;
390    if (!it.value().GetAsDictionary(&server_pref_dict)) {
391      DVLOG(1) << "Malformed http_server_properties server: " << server_str;
392      detected_corrupted_prefs = true;
393      continue;
394    }
395
396    // Get if server supports Spdy.
397    bool supports_spdy = false;
398    if ((server_pref_dict->GetBoolean("supports_spdy", &supports_spdy)) &&
399        supports_spdy) {
400      spdy_servers->push_back(server_str);
401    }
402
403    // Get SpdySettings.
404    DCHECK(spdy_settings_map->Peek(server) == spdy_settings_map->end());
405    const base::DictionaryValue* spdy_settings_dict = NULL;
406    if (server_pref_dict->GetDictionaryWithoutPathExpansion(
407            "settings", &spdy_settings_dict)) {
408      net::SettingsMap settings_map;
409      for (base::DictionaryValue::Iterator dict_it(*spdy_settings_dict);
410           !dict_it.IsAtEnd();
411           dict_it.Advance()) {
412        const std::string& id_str = dict_it.key();
413        int id = 0;
414        if (!base::StringToInt(id_str, &id)) {
415          DVLOG(1) << "Malformed id in SpdySettings for server: " << server_str;
416          NOTREACHED();
417          continue;
418        }
419        int value = 0;
420        if (!dict_it.value().GetAsInteger(&value)) {
421          DVLOG(1) << "Malformed value in SpdySettings for server: "
422                   << server_str;
423          NOTREACHED();
424          continue;
425        }
426        net::SettingsFlagsAndValue flags_and_value(net::SETTINGS_FLAG_PERSISTED,
427                                                   value);
428        settings_map[static_cast<net::SpdySettingsIds>(id)] = flags_and_value;
429      }
430      spdy_settings_map->Put(server, settings_map);
431    }
432
433    // Get alternate_protocol server.
434    DCHECK(alternate_protocol_map->Peek(server) ==
435           alternate_protocol_map->end());
436    const base::DictionaryValue* port_alternate_protocol_dict = NULL;
437    if (!server_pref_dict->GetDictionaryWithoutPathExpansion(
438            "alternate_protocol", &port_alternate_protocol_dict)) {
439      continue;
440    }
441
442    if (count >= alternate_protocols_to_load)
443      continue;
444    do {
445      int port = 0;
446      if (!port_alternate_protocol_dict->GetIntegerWithoutPathExpansion(
447              "port", &port) ||
448          (port > (1 << 16))) {
449        DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
450        detected_corrupted_prefs = true;
451        continue;
452      }
453      std::string protocol_str;
454      if (!port_alternate_protocol_dict->GetStringWithoutPathExpansion(
455              "protocol_str", &protocol_str)) {
456        DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
457        detected_corrupted_prefs = true;
458        continue;
459      }
460      net::AlternateProtocol protocol =
461          net::AlternateProtocolFromString(protocol_str);
462      if (!net::IsAlternateProtocolValid(protocol)) {
463        DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
464        detected_corrupted_prefs = true;
465        continue;
466      }
467
468      double probability = 1;
469      if (port_alternate_protocol_dict->HasKey("probability") &&
470          !port_alternate_protocol_dict->GetDoubleWithoutPathExpansion(
471              "probability", &probability)) {
472        DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
473        detected_corrupted_prefs = true;
474        continue;
475      }
476
477      net::AlternateProtocolInfo port_alternate_protocol(port,
478                                                         protocol,
479                                                         probability);
480      alternate_protocol_map->Put(server, port_alternate_protocol);
481      ++count;
482    } while (false);
483
484    // Get SupportsQuic.
485    DCHECK(supports_quic_map->find(server) == supports_quic_map->end());
486    const base::DictionaryValue* supports_quic_dict = NULL;
487    if (!server_pref_dict->GetDictionaryWithoutPathExpansion(
488            "supports_quic", &supports_quic_dict)) {
489      continue;
490    }
491    do {
492      bool used_quic = 0;
493      if (!supports_quic_dict->GetBooleanWithoutPathExpansion(
494              "used_quic", &used_quic)) {
495        DVLOG(1) << "Malformed SupportsQuic server: " << server_str;
496        detected_corrupted_prefs = true;
497        continue;
498      }
499      std::string address;
500      if (!supports_quic_dict->GetStringWithoutPathExpansion(
501              "address", &address)) {
502        DVLOG(1) << "Malformed SupportsQuic server: " << server_str;
503        detected_corrupted_prefs = true;
504        continue;
505      }
506      net::SupportsQuic supports_quic(used_quic, address);
507      supports_quic_map->insert(std::make_pair(server, supports_quic));
508    } while (false);
509  }
510
511  network_task_runner_->PostTask(
512      FROM_HERE,
513      base::Bind(
514          &HttpServerPropertiesManager::UpdateCacheFromPrefsOnNetworkThread,
515          base::Unretained(this),
516          base::Owned(spdy_servers.release()),
517          base::Owned(spdy_settings_map.release()),
518          base::Owned(alternate_protocol_map.release()),
519          alternate_protocol_experiment,
520          base::Owned(supports_quic_map.release()),
521          detected_corrupted_prefs));
522}
523
524void HttpServerPropertiesManager::UpdateCacheFromPrefsOnNetworkThread(
525    StringVector* spdy_servers,
526    net::SpdySettingsMap* spdy_settings_map,
527    net::AlternateProtocolMap* alternate_protocol_map,
528    net::AlternateProtocolExperiment alternate_protocol_experiment,
529    net::SupportsQuicMap* supports_quic_map,
530    bool detected_corrupted_prefs) {
531  // Preferences have the master data because admins might have pushed new
532  // preferences. Update the cached data with new data from preferences.
533  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
534
535  UMA_HISTOGRAM_COUNTS("Net.CountOfSpdyServers", spdy_servers->size());
536  http_server_properties_impl_->InitializeSpdyServers(spdy_servers, true);
537
538  // Update the cached data and use the new spdy_settings from preferences.
539  UMA_HISTOGRAM_COUNTS("Net.CountOfSpdySettings", spdy_settings_map->size());
540  http_server_properties_impl_->InitializeSpdySettingsServers(
541      spdy_settings_map);
542
543  // Update the cached data and use the new Alternate-Protocol server list from
544  // preferences.
545  UMA_HISTOGRAM_COUNTS("Net.CountOfAlternateProtocolServers",
546                       alternate_protocol_map->size());
547  http_server_properties_impl_->InitializeAlternateProtocolServers(
548      alternate_protocol_map);
549  http_server_properties_impl_->SetAlternateProtocolExperiment(
550      alternate_protocol_experiment);
551
552  http_server_properties_impl_->InitializeSupportsQuic(supports_quic_map);
553
554  // Update the prefs with what we have read (delete all corrupted prefs).
555  if (detected_corrupted_prefs)
556    ScheduleUpdatePrefsOnNetworkThread();
557}
558
559//
560// Update Preferences with data from the cached data.
561//
562void HttpServerPropertiesManager::ScheduleUpdatePrefsOnNetworkThread() {
563  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
564  // Cancel pending updates, if any.
565  network_prefs_update_timer_->Stop();
566  StartPrefsUpdateTimerOnNetworkThread(
567      base::TimeDelta::FromMilliseconds(kUpdatePrefsDelayMs));
568}
569
570void HttpServerPropertiesManager::StartPrefsUpdateTimerOnNetworkThread(
571    base::TimeDelta delay) {
572  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
573  // This is overridden in tests to post the task without the delay.
574  network_prefs_update_timer_->Start(
575      FROM_HERE,
576      delay,
577      this,
578      &HttpServerPropertiesManager::UpdatePrefsFromCacheOnNetworkThread);
579}
580
581// This is required so we can set this as the callback for a timer.
582void HttpServerPropertiesManager::UpdatePrefsFromCacheOnNetworkThread() {
583  UpdatePrefsFromCacheOnNetworkThread(base::Closure());
584}
585
586void HttpServerPropertiesManager::UpdatePrefsFromCacheOnNetworkThread(
587    const base::Closure& completion) {
588  DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
589
590  base::ListValue* spdy_server_list = new base::ListValue;
591  http_server_properties_impl_->GetSpdyServerList(
592      spdy_server_list, kMaxSupportsSpdyServerHostsToPersist);
593
594  net::SpdySettingsMap* spdy_settings_map =
595      new net::SpdySettingsMap(kMaxSpdySettingsHostsToPersist);
596  const net::SpdySettingsMap& main_map =
597      http_server_properties_impl_->spdy_settings_map();
598  int count = 0;
599  for (net::SpdySettingsMap::const_iterator it = main_map.begin();
600       it != main_map.end() && count < kMaxSpdySettingsHostsToPersist;
601       ++it, ++count) {
602    spdy_settings_map->Put(it->first, it->second);
603  }
604
605  net::AlternateProtocolMap* alternate_protocol_map =
606      new net::AlternateProtocolMap(kMaxAlternateProtocolHostsToPersist);
607  const net::AlternateProtocolMap& map =
608      http_server_properties_impl_->alternate_protocol_map();
609  count = 0;
610  typedef std::map<std::string, bool> CanonicalHostPersistedMap;
611  CanonicalHostPersistedMap persisted_map;
612  for (net::AlternateProtocolMap::const_iterator it = map.begin();
613       it != map.end() && count < kMaxAlternateProtocolHostsToPersist;
614       ++it) {
615    const net::HostPortPair& server = it->first;
616    std::string canonical_suffix =
617        http_server_properties_impl_->GetCanonicalSuffix(server);
618    if (!canonical_suffix.empty()) {
619      if (persisted_map.find(canonical_suffix) != persisted_map.end())
620        continue;
621      persisted_map[canonical_suffix] = true;
622    }
623    alternate_protocol_map->Put(server, it->second);
624    ++count;
625  }
626
627  net::SupportsQuicMap* supports_quic_map = new net::SupportsQuicMap();
628  const net::SupportsQuicMap& main_supports_quic_map =
629      http_server_properties_impl_->supports_quic_map();
630  for (net::SupportsQuicMap::const_iterator it = main_supports_quic_map.begin();
631       it != main_supports_quic_map.end(); ++it) {
632    supports_quic_map->insert(std::make_pair(it->first, it->second));
633  }
634
635  // Update the preferences on the pref thread.
636  pref_task_runner_->PostTask(
637      FROM_HERE,
638      base::Bind(&HttpServerPropertiesManager::UpdatePrefsOnPrefThread,
639                 pref_weak_ptr_,
640                 base::Owned(spdy_server_list),
641                 base::Owned(spdy_settings_map),
642                 base::Owned(alternate_protocol_map),
643                 base::Owned(supports_quic_map),
644                 completion));
645}
646
647// A local or temporary data structure to hold |supports_spdy|, SpdySettings,
648// AlternateProtocolInfo and SupportsQuic preferences for a server. This is used
649// only in UpdatePrefsOnPrefThread.
650struct ServerPref {
651  ServerPref() : supports_spdy(false),
652                 settings_map(NULL),
653                 alternate_protocol(NULL),
654                 supports_quic(NULL) {}
655  ServerPref(bool supports_spdy,
656             const net::SettingsMap* settings_map,
657             const net::AlternateProtocolInfo* alternate_protocol,
658             const net::SupportsQuic* supports_quic)
659      : supports_spdy(supports_spdy),
660        settings_map(settings_map),
661        alternate_protocol(alternate_protocol),
662        supports_quic(supports_quic) {}
663  bool supports_spdy;
664  const net::SettingsMap* settings_map;
665  const net::AlternateProtocolInfo* alternate_protocol;
666  const net::SupportsQuic* supports_quic;
667};
668
669void HttpServerPropertiesManager::UpdatePrefsOnPrefThread(
670    base::ListValue* spdy_server_list,
671    net::SpdySettingsMap* spdy_settings_map,
672    net::AlternateProtocolMap* alternate_protocol_map,
673    net::SupportsQuicMap* supports_quic_map,
674    const base::Closure& completion) {
675  typedef std::map<net::HostPortPair, ServerPref> ServerPrefMap;
676  ServerPrefMap server_pref_map;
677
678  DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
679
680  // Add servers that support spdy to server_pref_map.
681  std::string s;
682  for (base::ListValue::const_iterator list_it = spdy_server_list->begin();
683       list_it != spdy_server_list->end();
684       ++list_it) {
685    if ((*list_it)->GetAsString(&s)) {
686      net::HostPortPair server = net::HostPortPair::FromString(s);
687
688      ServerPrefMap::iterator it = server_pref_map.find(server);
689      if (it == server_pref_map.end()) {
690        ServerPref server_pref(true, NULL, NULL, NULL);
691        server_pref_map[server] = server_pref;
692      } else {
693        it->second.supports_spdy = true;
694      }
695    }
696  }
697
698  // Add servers that have SpdySettings to server_pref_map.
699  for (net::SpdySettingsMap::iterator map_it = spdy_settings_map->begin();
700       map_it != spdy_settings_map->end();
701       ++map_it) {
702    const net::HostPortPair& server = map_it->first;
703
704    ServerPrefMap::iterator it = server_pref_map.find(server);
705    if (it == server_pref_map.end()) {
706      ServerPref server_pref(false, &map_it->second, NULL, NULL);
707      server_pref_map[server] = server_pref;
708    } else {
709      it->second.settings_map = &map_it->second;
710    }
711  }
712
713  // Add AlternateProtocol servers to server_pref_map.
714  for (net::AlternateProtocolMap::const_iterator map_it =
715           alternate_protocol_map->begin();
716       map_it != alternate_protocol_map->end();
717       ++map_it) {
718    const net::HostPortPair& server = map_it->first;
719    const net::AlternateProtocolInfo& port_alternate_protocol =
720        map_it->second;
721    if (!net::IsAlternateProtocolValid(port_alternate_protocol.protocol)) {
722      continue;
723    }
724
725    ServerPrefMap::iterator it = server_pref_map.find(server);
726    if (it == server_pref_map.end()) {
727      ServerPref server_pref(false, NULL, &map_it->second, NULL);
728      server_pref_map[server] = server_pref;
729    } else {
730      it->second.alternate_protocol = &map_it->second;
731    }
732  }
733
734  // Add SupportsQuic servers to server_pref_map.
735  for (net::SupportsQuicMap::const_iterator map_it = supports_quic_map->begin();
736       map_it != supports_quic_map->end(); ++map_it) {
737    const net::HostPortPair& server = map_it->first;
738
739    ServerPrefMap::iterator it = server_pref_map.find(server);
740    if (it == server_pref_map.end()) {
741      ServerPref server_pref(false, NULL, NULL, &map_it->second);
742      server_pref_map[server] = server_pref;
743    } else {
744      it->second.supports_quic = &map_it->second;
745    }
746  }
747
748  // Persist properties to the |path_|.
749  base::DictionaryValue http_server_properties_dict;
750  base::DictionaryValue* servers_dict = new base::DictionaryValue;
751  for (ServerPrefMap::const_iterator map_it = server_pref_map.begin();
752       map_it != server_pref_map.end();
753       ++map_it) {
754    const net::HostPortPair& server = map_it->first;
755    const ServerPref& server_pref = map_it->second;
756
757    base::DictionaryValue* server_pref_dict = new base::DictionaryValue;
758
759    // Save supports_spdy.
760    if (server_pref.supports_spdy)
761      server_pref_dict->SetBoolean("supports_spdy", server_pref.supports_spdy);
762
763    // Save SPDY settings.
764    if (server_pref.settings_map) {
765      base::DictionaryValue* spdy_settings_dict = new base::DictionaryValue;
766      for (net::SettingsMap::const_iterator it =
767               server_pref.settings_map->begin();
768           it != server_pref.settings_map->end();
769           ++it) {
770        net::SpdySettingsIds id = it->first;
771        uint32 value = it->second.second;
772        std::string key = base::StringPrintf("%u", id);
773        spdy_settings_dict->SetInteger(key, value);
774      }
775      server_pref_dict->SetWithoutPathExpansion("settings", spdy_settings_dict);
776    }
777
778    // Save alternate_protocol.
779    if (server_pref.alternate_protocol) {
780      base::DictionaryValue* port_alternate_protocol_dict =
781          new base::DictionaryValue;
782      const net::AlternateProtocolInfo* port_alternate_protocol =
783          server_pref.alternate_protocol;
784      port_alternate_protocol_dict->SetInteger("port",
785                                               port_alternate_protocol->port);
786      const char* protocol_str =
787          net::AlternateProtocolToString(port_alternate_protocol->protocol);
788      port_alternate_protocol_dict->SetString("protocol_str", protocol_str);
789      port_alternate_protocol_dict->SetDouble(
790          "probability", port_alternate_protocol->probability);
791      server_pref_dict->SetWithoutPathExpansion(
792          "alternate_protocol", port_alternate_protocol_dict);
793    }
794
795    // Save supports_quic.
796    if (server_pref.supports_quic) {
797      base::DictionaryValue* supports_quic_dict = new base::DictionaryValue;
798      const net::SupportsQuic* supports_quic = server_pref.supports_quic;
799      supports_quic_dict->SetBoolean("used_quic", supports_quic->used_quic);
800      supports_quic_dict->SetString("address", supports_quic->address);
801      server_pref_dict->SetWithoutPathExpansion(
802          "supports_quic", supports_quic_dict);
803    }
804
805    servers_dict->SetWithoutPathExpansion(server.ToString(), server_pref_dict);
806  }
807
808  http_server_properties_dict.SetWithoutPathExpansion("servers", servers_dict);
809  SetVersion(&http_server_properties_dict, kVersionNumber);
810  setting_prefs_ = true;
811  pref_service_->Set(path_, http_server_properties_dict);
812  setting_prefs_ = false;
813
814  // Note that |completion| will be fired after we have written everything to
815  // the Preferences, but likely before these changes are serialized to disk.
816  // This is not a problem though, as JSONPrefStore guarantees that this will
817  // happen, pretty soon, and even in the case we shut down immediately.
818  if (!completion.is_null())
819    completion.Run();
820}
821
822void HttpServerPropertiesManager::OnHttpServerPropertiesChanged() {
823  DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
824  if (!setting_prefs_)
825    ScheduleUpdateCacheOnPrefThread();
826}
827
828}  // namespace net
829