summaryrefslogtreecommitdiffstats
path: root/net/http
diff options
context:
space:
mode:
authormef@chromium.org <mef@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-10 12:43:47 +0000
committermef@chromium.org <mef@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-10 12:43:47 +0000
commitad66db1afee6c9be75a7de80901fd774b6cf027b (patch)
tree16c32dd9f737919dfe1883d422b4c7add11eb65d /net/http
parent8ca02c0ba49bd0e7ee1fc7a9619dce8fa72fbaea (diff)
downloadchromium_src-ad66db1afee6c9be75a7de80901fd774b6cf027b.zip
chromium_src-ad66db1afee6c9be75a7de80901fd774b6cf027b.tar.gz
chromium_src-ad66db1afee6c9be75a7de80901fd774b6cf027b.tar.bz2
Move http_server_properties_manager from chrome/browser/net to net/http.
BUG= Review URL: https://codereview.chromium.org/378823002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282319 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http')
-rw-r--r--net/http/http_server_properties_manager.cc734
-rw-r--r--net/http/http_server_properties_manager.h263
-rw-r--r--net/http/http_server_properties_manager_unittest.cc484
3 files changed, 1481 insertions, 0 deletions
diff --git a/net/http/http_server_properties_manager.cc b/net/http/http_server_properties_manager.cc
new file mode 100644
index 0000000..35458ca
--- /dev/null
+++ b/net/http/http_server_properties_manager.cc
@@ -0,0 +1,734 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http/http_server_properties_manager.h"
+
+#include "base/bind.h"
+#include "base/metrics/histogram.h"
+#include "base/prefs/pref_service.h"
+#include "base/rand_util.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/values.h"
+
+namespace net {
+
+namespace {
+
+// Time to wait before starting an update the http_server_properties_impl_ cache
+// from preferences. Scheduling another update during this period will reset the
+// timer.
+const int64 kUpdateCacheDelayMs = 1000;
+
+// Time to wait before starting an update the preferences from the
+// http_server_properties_impl_ cache. Scheduling another update during this
+// period will reset the timer.
+const int64 kUpdatePrefsDelayMs = 5000;
+
+// "version" 0 indicates, http_server_properties doesn't have "version"
+// property.
+const int kMissingVersion = 0;
+
+// The version number of persisted http_server_properties.
+const int kVersionNumber = 3;
+
+typedef std::vector<std::string> StringVector;
+
+// Load either 200 or 1000 servers based on a coin flip.
+const int k200AlternateProtocolHostsToLoad = 200;
+const int k1000AlternateProtocolHostsToLoad = 1000;
+// Persist 1000 MRU AlternateProtocolHostPortPairs.
+const int kMaxAlternateProtocolHostsToPersist = 1000;
+
+// Persist 200 MRU SpdySettingsHostPortPairs.
+const int kMaxSpdySettingsHostsToPersist = 200;
+
+// Persist 300 MRU SupportsSpdyServerHostPortPairs.
+const int kMaxSupportsSpdyServerHostsToPersist = 300;
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// HttpServerPropertiesManager
+
+HttpServerPropertiesManager::HttpServerPropertiesManager(
+ PrefService* pref_service,
+ const char* pref_path,
+ scoped_refptr<base::SequencedTaskRunner> network_task_runner)
+ : pref_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ pref_service_(pref_service),
+ setting_prefs_(false),
+ path_(pref_path),
+ network_task_runner_(network_task_runner) {
+ DCHECK(pref_service);
+ pref_weak_ptr_factory_.reset(
+ new base::WeakPtrFactory<HttpServerPropertiesManager>(this));
+ pref_weak_ptr_ = pref_weak_ptr_factory_->GetWeakPtr();
+ pref_cache_update_timer_.reset(
+ new base::OneShotTimer<HttpServerPropertiesManager>);
+ pref_change_registrar_.Init(pref_service_);
+ pref_change_registrar_.Add(
+ path_,
+ base::Bind(&HttpServerPropertiesManager::OnHttpServerPropertiesChanged,
+ base::Unretained(this)));
+}
+
+HttpServerPropertiesManager::~HttpServerPropertiesManager() {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ network_weak_ptr_factory_.reset();
+}
+
+void HttpServerPropertiesManager::InitializeOnNetworkThread() {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ network_weak_ptr_factory_.reset(
+ new base::WeakPtrFactory<HttpServerPropertiesManager>(this));
+ http_server_properties_impl_.reset(new net::HttpServerPropertiesImpl());
+
+ network_prefs_update_timer_.reset(
+ new base::OneShotTimer<HttpServerPropertiesManager>);
+
+ pref_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&HttpServerPropertiesManager::UpdateCacheFromPrefsOnPrefThread,
+ pref_weak_ptr_));
+}
+
+void HttpServerPropertiesManager::ShutdownOnPrefThread() {
+ DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
+ // Cancel any pending updates, and stop listening for pref change updates.
+ pref_cache_update_timer_->Stop();
+ pref_weak_ptr_factory_.reset();
+ pref_change_registrar_.RemoveAll();
+}
+
+// static
+void HttpServerPropertiesManager::SetVersion(
+ base::DictionaryValue* http_server_properties_dict,
+ int version_number) {
+ if (version_number < 0)
+ version_number = kVersionNumber;
+ DCHECK_LE(version_number, kVersionNumber);
+ if (version_number <= kVersionNumber)
+ http_server_properties_dict->SetInteger("version", version_number);
+}
+
+// This is required for conformance with the HttpServerProperties interface.
+base::WeakPtr<net::HttpServerProperties>
+HttpServerPropertiesManager::GetWeakPtr() {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ return network_weak_ptr_factory_->GetWeakPtr();
+}
+
+void HttpServerPropertiesManager::Clear() {
+ Clear(base::Closure());
+}
+
+void HttpServerPropertiesManager::Clear(const base::Closure& completion) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+
+ http_server_properties_impl_->Clear();
+ UpdatePrefsFromCacheOnNetworkThread(completion);
+}
+
+bool HttpServerPropertiesManager::SupportsSpdy(
+ const net::HostPortPair& server) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ return http_server_properties_impl_->SupportsSpdy(server);
+}
+
+void HttpServerPropertiesManager::SetSupportsSpdy(
+ const net::HostPortPair& server,
+ bool support_spdy) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+
+ http_server_properties_impl_->SetSupportsSpdy(server, support_spdy);
+ ScheduleUpdatePrefsOnNetworkThread();
+}
+
+bool HttpServerPropertiesManager::HasAlternateProtocol(
+ const net::HostPortPair& server) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ return http_server_properties_impl_->HasAlternateProtocol(server);
+}
+
+net::AlternateProtocolInfo
+HttpServerPropertiesManager::GetAlternateProtocol(
+ const net::HostPortPair& server) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ return http_server_properties_impl_->GetAlternateProtocol(server);
+}
+
+void HttpServerPropertiesManager::SetAlternateProtocol(
+ const net::HostPortPair& server,
+ uint16 alternate_port,
+ AlternateProtocol alternate_protocol,
+ double alternate_probability) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ http_server_properties_impl_->SetAlternateProtocol(
+ server, alternate_port, alternate_protocol, alternate_probability);
+ ScheduleUpdatePrefsOnNetworkThread();
+}
+
+void HttpServerPropertiesManager::SetBrokenAlternateProtocol(
+ const net::HostPortPair& server) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ http_server_properties_impl_->SetBrokenAlternateProtocol(server);
+ ScheduleUpdatePrefsOnNetworkThread();
+}
+
+bool HttpServerPropertiesManager::WasAlternateProtocolRecentlyBroken(
+ const net::HostPortPair& server) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ return http_server_properties_impl_->WasAlternateProtocolRecentlyBroken(
+ server);
+}
+
+void HttpServerPropertiesManager::ConfirmAlternateProtocol(
+ const net::HostPortPair& server) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ http_server_properties_impl_->ConfirmAlternateProtocol(server);
+ ScheduleUpdatePrefsOnNetworkThread();
+}
+
+void HttpServerPropertiesManager::ClearAlternateProtocol(
+ const net::HostPortPair& server) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ http_server_properties_impl_->ClearAlternateProtocol(server);
+ ScheduleUpdatePrefsOnNetworkThread();
+}
+
+const net::AlternateProtocolMap&
+HttpServerPropertiesManager::alternate_protocol_map() const {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ return http_server_properties_impl_->alternate_protocol_map();
+}
+
+void HttpServerPropertiesManager::SetAlternateProtocolExperiment(
+ AlternateProtocolExperiment experiment) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ http_server_properties_impl_->SetAlternateProtocolExperiment(experiment);
+}
+
+void HttpServerPropertiesManager::SetAlternateProtocolProbabilityThreshold(
+ double threshold) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ http_server_properties_impl_->SetAlternateProtocolProbabilityThreshold(
+ threshold);
+}
+
+AlternateProtocolExperiment
+HttpServerPropertiesManager::GetAlternateProtocolExperiment() const {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ return http_server_properties_impl_->GetAlternateProtocolExperiment();
+}
+
+const SettingsMap& HttpServerPropertiesManager::GetSpdySettings(
+ const HostPortPair& host_port_pair) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ return http_server_properties_impl_->GetSpdySettings(host_port_pair);
+}
+
+bool HttpServerPropertiesManager::SetSpdySetting(
+ const HostPortPair& host_port_pair,
+ SpdySettingsIds id,
+ SpdySettingsFlags flags,
+ uint32 value) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ bool persist = http_server_properties_impl_->SetSpdySetting(
+ host_port_pair, id, flags, value);
+ if (persist)
+ ScheduleUpdatePrefsOnNetworkThread();
+ return persist;
+}
+
+void HttpServerPropertiesManager::ClearSpdySettings(
+ const HostPortPair& host_port_pair) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ http_server_properties_impl_->ClearSpdySettings(host_port_pair);
+ ScheduleUpdatePrefsOnNetworkThread();
+}
+
+void HttpServerPropertiesManager::ClearAllSpdySettings() {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ http_server_properties_impl_->ClearAllSpdySettings();
+ ScheduleUpdatePrefsOnNetworkThread();
+}
+
+const SpdySettingsMap& HttpServerPropertiesManager::spdy_settings_map()
+ const {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ return http_server_properties_impl_->spdy_settings_map();
+}
+
+void HttpServerPropertiesManager::SetServerNetworkStats(
+ const net::HostPortPair& host_port_pair,
+ NetworkStats stats) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ http_server_properties_impl_->SetServerNetworkStats(host_port_pair, stats);
+}
+
+const HttpServerPropertiesManager::NetworkStats*
+HttpServerPropertiesManager::GetServerNetworkStats(
+ const net::HostPortPair& host_port_pair) const {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ return http_server_properties_impl_->GetServerNetworkStats(host_port_pair);
+}
+
+//
+// Update the HttpServerPropertiesImpl's cache with data from preferences.
+//
+void HttpServerPropertiesManager::ScheduleUpdateCacheOnPrefThread() {
+ DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
+ // Cancel pending updates, if any.
+ pref_cache_update_timer_->Stop();
+ StartCacheUpdateTimerOnPrefThread(
+ base::TimeDelta::FromMilliseconds(kUpdateCacheDelayMs));
+}
+
+void HttpServerPropertiesManager::StartCacheUpdateTimerOnPrefThread(
+ base::TimeDelta delay) {
+ DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
+ pref_cache_update_timer_->Start(
+ FROM_HERE,
+ delay,
+ this,
+ &HttpServerPropertiesManager::UpdateCacheFromPrefsOnPrefThread);
+}
+
+void HttpServerPropertiesManager::UpdateCacheFromPrefsOnPrefThread() {
+ // The preferences can only be read on the pref thread.
+ DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
+
+ if (!pref_service_->HasPrefPath(path_))
+ return;
+
+ bool detected_corrupted_prefs = false;
+ const base::DictionaryValue& http_server_properties_dict =
+ *pref_service_->GetDictionary(path_);
+
+ int version = kMissingVersion;
+ if (!http_server_properties_dict.GetIntegerWithoutPathExpansion("version",
+ &version)) {
+ DVLOG(1) << "Missing version. Clearing all properties.";
+ return;
+ }
+
+ // The properties for a given server is in
+ // http_server_properties_dict["servers"][server].
+ const base::DictionaryValue* servers_dict = NULL;
+ if (!http_server_properties_dict.GetDictionaryWithoutPathExpansion(
+ "servers", &servers_dict)) {
+ DVLOG(1) << "Malformed http_server_properties for servers.";
+ return;
+ }
+
+ // String is host/port pair of spdy server.
+ scoped_ptr<StringVector> spdy_servers(new StringVector);
+ scoped_ptr<net::SpdySettingsMap> spdy_settings_map(
+ new net::SpdySettingsMap(kMaxSpdySettingsHostsToPersist));
+ scoped_ptr<net::AlternateProtocolMap> alternate_protocol_map(
+ new net::AlternateProtocolMap(kMaxAlternateProtocolHostsToPersist));
+ // TODO(rtenneti): Delete the following code after the experiment.
+ int alternate_protocols_to_load = k200AlternateProtocolHostsToLoad;
+ net::AlternateProtocolExperiment alternate_protocol_experiment =
+ net::ALTERNATE_PROTOCOL_NOT_PART_OF_EXPERIMENT;
+ if (version == kVersionNumber) {
+ if (base::RandInt(0, 99) == 0) {
+ alternate_protocol_experiment =
+ net::ALTERNATE_PROTOCOL_TRUNCATED_200_SERVERS;
+ } else {
+ alternate_protocols_to_load = k1000AlternateProtocolHostsToLoad;
+ alternate_protocol_experiment =
+ net::ALTERNATE_PROTOCOL_TRUNCATED_1000_SERVERS;
+ }
+ DVLOG(1) << "# of servers that support alternate_protocol: "
+ << alternate_protocols_to_load;
+ }
+
+ int count = 0;
+ for (base::DictionaryValue::Iterator it(*servers_dict); !it.IsAtEnd();
+ it.Advance()) {
+ // Get server's host/pair.
+ const std::string& server_str = it.key();
+ net::HostPortPair server = net::HostPortPair::FromString(server_str);
+ if (server.host().empty()) {
+ DVLOG(1) << "Malformed http_server_properties for server: " << server_str;
+ detected_corrupted_prefs = true;
+ continue;
+ }
+
+ const base::DictionaryValue* server_pref_dict = NULL;
+ if (!it.value().GetAsDictionary(&server_pref_dict)) {
+ DVLOG(1) << "Malformed http_server_properties server: " << server_str;
+ detected_corrupted_prefs = true;
+ continue;
+ }
+
+ // Get if server supports Spdy.
+ bool supports_spdy = false;
+ if ((server_pref_dict->GetBoolean("supports_spdy", &supports_spdy)) &&
+ supports_spdy) {
+ spdy_servers->push_back(server_str);
+ }
+
+ // Get SpdySettings.
+ DCHECK(spdy_settings_map->Peek(server) == spdy_settings_map->end());
+ const base::DictionaryValue* spdy_settings_dict = NULL;
+ if (server_pref_dict->GetDictionaryWithoutPathExpansion(
+ "settings", &spdy_settings_dict)) {
+ net::SettingsMap settings_map;
+ for (base::DictionaryValue::Iterator dict_it(*spdy_settings_dict);
+ !dict_it.IsAtEnd();
+ dict_it.Advance()) {
+ const std::string& id_str = dict_it.key();
+ int id = 0;
+ if (!base::StringToInt(id_str, &id)) {
+ DVLOG(1) << "Malformed id in SpdySettings for server: " << server_str;
+ NOTREACHED();
+ continue;
+ }
+ int value = 0;
+ if (!dict_it.value().GetAsInteger(&value)) {
+ DVLOG(1) << "Malformed value in SpdySettings for server: "
+ << server_str;
+ NOTREACHED();
+ continue;
+ }
+ net::SettingsFlagsAndValue flags_and_value(net::SETTINGS_FLAG_PERSISTED,
+ value);
+ settings_map[static_cast<net::SpdySettingsIds>(id)] = flags_and_value;
+ }
+ spdy_settings_map->Put(server, settings_map);
+ }
+
+ // Get alternate_protocol server.
+ DCHECK(alternate_protocol_map->Peek(server) ==
+ alternate_protocol_map->end());
+ const base::DictionaryValue* port_alternate_protocol_dict = NULL;
+ if (!server_pref_dict->GetDictionaryWithoutPathExpansion(
+ "alternate_protocol", &port_alternate_protocol_dict)) {
+ continue;
+ }
+
+ if (count >= alternate_protocols_to_load)
+ continue;
+ do {
+ int port = 0;
+ if (!port_alternate_protocol_dict->GetIntegerWithoutPathExpansion(
+ "port", &port) ||
+ (port > (1 << 16))) {
+ DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
+ detected_corrupted_prefs = true;
+ continue;
+ }
+ std::string protocol_str;
+ if (!port_alternate_protocol_dict->GetStringWithoutPathExpansion(
+ "protocol_str", &protocol_str)) {
+ DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
+ detected_corrupted_prefs = true;
+ continue;
+ }
+ net::AlternateProtocol protocol =
+ net::AlternateProtocolFromString(protocol_str);
+ if (!net::IsAlternateProtocolValid(protocol)) {
+ DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
+ detected_corrupted_prefs = true;
+ continue;
+ }
+
+ double probability = 1;
+ if (port_alternate_protocol_dict->HasKey("probability") &&
+ !port_alternate_protocol_dict->GetDoubleWithoutPathExpansion(
+ "probability", &probability)) {
+ DVLOG(1) << "Malformed Alternate-Protocol server: " << server_str;
+ detected_corrupted_prefs = true;
+ continue;
+ }
+
+ net::AlternateProtocolInfo port_alternate_protocol(port,
+ protocol,
+ probability);
+ alternate_protocol_map->Put(server, port_alternate_protocol);
+ ++count;
+ } while (false);
+ }
+
+ network_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &HttpServerPropertiesManager::UpdateCacheFromPrefsOnNetworkThread,
+ base::Unretained(this),
+ base::Owned(spdy_servers.release()),
+ base::Owned(spdy_settings_map.release()),
+ base::Owned(alternate_protocol_map.release()),
+ alternate_protocol_experiment,
+ detected_corrupted_prefs));
+}
+
+void HttpServerPropertiesManager::UpdateCacheFromPrefsOnNetworkThread(
+ StringVector* spdy_servers,
+ net::SpdySettingsMap* spdy_settings_map,
+ net::AlternateProtocolMap* alternate_protocol_map,
+ net::AlternateProtocolExperiment alternate_protocol_experiment,
+ bool detected_corrupted_prefs) {
+ // Preferences have the master data because admins might have pushed new
+ // preferences. Update the cached data with new data from preferences.
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+
+ UMA_HISTOGRAM_COUNTS("Net.CountOfSpdyServers", spdy_servers->size());
+ http_server_properties_impl_->InitializeSpdyServers(spdy_servers, true);
+
+ // Update the cached data and use the new spdy_settings from preferences.
+ UMA_HISTOGRAM_COUNTS("Net.CountOfSpdySettings", spdy_settings_map->size());
+ http_server_properties_impl_->InitializeSpdySettingsServers(
+ spdy_settings_map);
+
+ // Update the cached data and use the new Alternate-Protocol server list from
+ // preferences.
+ UMA_HISTOGRAM_COUNTS("Net.CountOfAlternateProtocolServers",
+ alternate_protocol_map->size());
+ http_server_properties_impl_->InitializeAlternateProtocolServers(
+ alternate_protocol_map);
+ http_server_properties_impl_->SetAlternateProtocolExperiment(
+ alternate_protocol_experiment);
+
+ // Update the prefs with what we have read (delete all corrupted prefs).
+ if (detected_corrupted_prefs)
+ ScheduleUpdatePrefsOnNetworkThread();
+}
+
+//
+// Update Preferences with data from the cached data.
+//
+void HttpServerPropertiesManager::ScheduleUpdatePrefsOnNetworkThread() {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ // Cancel pending updates, if any.
+ network_prefs_update_timer_->Stop();
+ StartPrefsUpdateTimerOnNetworkThread(
+ base::TimeDelta::FromMilliseconds(kUpdatePrefsDelayMs));
+}
+
+void HttpServerPropertiesManager::StartPrefsUpdateTimerOnNetworkThread(
+ base::TimeDelta delay) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+ // This is overridden in tests to post the task without the delay.
+ network_prefs_update_timer_->Start(
+ FROM_HERE,
+ delay,
+ this,
+ &HttpServerPropertiesManager::UpdatePrefsFromCacheOnNetworkThread);
+}
+
+// This is required so we can set this as the callback for a timer.
+void HttpServerPropertiesManager::UpdatePrefsFromCacheOnNetworkThread() {
+ UpdatePrefsFromCacheOnNetworkThread(base::Closure());
+}
+
+void HttpServerPropertiesManager::UpdatePrefsFromCacheOnNetworkThread(
+ const base::Closure& completion) {
+ DCHECK(network_task_runner_->RunsTasksOnCurrentThread());
+
+ base::ListValue* spdy_server_list = new base::ListValue;
+ http_server_properties_impl_->GetSpdyServerList(
+ spdy_server_list, kMaxSupportsSpdyServerHostsToPersist);
+
+ net::SpdySettingsMap* spdy_settings_map =
+ new net::SpdySettingsMap(kMaxSpdySettingsHostsToPersist);
+ const net::SpdySettingsMap& main_map =
+ http_server_properties_impl_->spdy_settings_map();
+ int count = 0;
+ for (net::SpdySettingsMap::const_iterator it = main_map.begin();
+ it != main_map.end() && count < kMaxSpdySettingsHostsToPersist;
+ ++it, ++count) {
+ spdy_settings_map->Put(it->first, it->second);
+ }
+
+ net::AlternateProtocolMap* alternate_protocol_map =
+ new net::AlternateProtocolMap(kMaxAlternateProtocolHostsToPersist);
+ const net::AlternateProtocolMap& map =
+ http_server_properties_impl_->alternate_protocol_map();
+ count = 0;
+ typedef std::map<std::string, bool> CanonicalHostPersistedMap;
+ CanonicalHostPersistedMap persisted_map;
+ for (net::AlternateProtocolMap::const_iterator it = map.begin();
+ it != map.end() && count < kMaxAlternateProtocolHostsToPersist;
+ ++it) {
+ const net::HostPortPair& server = it->first;
+ std::string canonical_suffix =
+ http_server_properties_impl_->GetCanonicalSuffix(server);
+ if (!canonical_suffix.empty()) {
+ if (persisted_map.find(canonical_suffix) != persisted_map.end())
+ continue;
+ persisted_map[canonical_suffix] = true;
+ }
+ alternate_protocol_map->Put(server, it->second);
+ ++count;
+ }
+
+ // Update the preferences on the pref thread.
+ pref_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&HttpServerPropertiesManager::UpdatePrefsOnPrefThread,
+ pref_weak_ptr_,
+ base::Owned(spdy_server_list),
+ base::Owned(spdy_settings_map),
+ base::Owned(alternate_protocol_map),
+ completion));
+}
+
+// A local or temporary data structure to hold |supports_spdy|, SpdySettings,
+// and AlternateProtocolInfo preferences for a server. This is used only in
+// UpdatePrefsOnPrefThread.
+struct ServerPref {
+ ServerPref()
+ : supports_spdy(false), settings_map(NULL), alternate_protocol(NULL) {}
+ ServerPref(bool supports_spdy,
+ const net::SettingsMap* settings_map,
+ const net::AlternateProtocolInfo* alternate_protocol)
+ : supports_spdy(supports_spdy),
+ settings_map(settings_map),
+ alternate_protocol(alternate_protocol) {}
+ bool supports_spdy;
+ const net::SettingsMap* settings_map;
+ const net::AlternateProtocolInfo* alternate_protocol;
+};
+
+void HttpServerPropertiesManager::UpdatePrefsOnPrefThread(
+ base::ListValue* spdy_server_list,
+ net::SpdySettingsMap* spdy_settings_map,
+ net::AlternateProtocolMap* alternate_protocol_map,
+ const base::Closure& completion) {
+ typedef std::map<net::HostPortPair, ServerPref> ServerPrefMap;
+ ServerPrefMap server_pref_map;
+
+ DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
+
+ // Add servers that support spdy to server_pref_map.
+ std::string s;
+ for (base::ListValue::const_iterator list_it = spdy_server_list->begin();
+ list_it != spdy_server_list->end();
+ ++list_it) {
+ if ((*list_it)->GetAsString(&s)) {
+ net::HostPortPair server = net::HostPortPair::FromString(s);
+
+ ServerPrefMap::iterator it = server_pref_map.find(server);
+ if (it == server_pref_map.end()) {
+ ServerPref server_pref(true, NULL, NULL);
+ server_pref_map[server] = server_pref;
+ } else {
+ it->second.supports_spdy = true;
+ }
+ }
+ }
+
+ // Add servers that have SpdySettings to server_pref_map.
+ for (net::SpdySettingsMap::iterator map_it = spdy_settings_map->begin();
+ map_it != spdy_settings_map->end();
+ ++map_it) {
+ const net::HostPortPair& server = map_it->first;
+
+ ServerPrefMap::iterator it = server_pref_map.find(server);
+ if (it == server_pref_map.end()) {
+ ServerPref server_pref(false, &map_it->second, NULL);
+ server_pref_map[server] = server_pref;
+ } else {
+ it->second.settings_map = &map_it->second;
+ }
+ }
+
+ // Add AlternateProtocol servers to server_pref_map.
+ for (net::AlternateProtocolMap::const_iterator map_it =
+ alternate_protocol_map->begin();
+ map_it != alternate_protocol_map->end();
+ ++map_it) {
+ const net::HostPortPair& server = map_it->first;
+ const net::AlternateProtocolInfo& port_alternate_protocol =
+ map_it->second;
+ if (!net::IsAlternateProtocolValid(port_alternate_protocol.protocol)) {
+ continue;
+ }
+
+ ServerPrefMap::iterator it = server_pref_map.find(server);
+ if (it == server_pref_map.end()) {
+ ServerPref server_pref(false, NULL, &map_it->second);
+ server_pref_map[server] = server_pref;
+ } else {
+ it->second.alternate_protocol = &map_it->second;
+ }
+ }
+
+ // Persist properties to the |path_|.
+ base::DictionaryValue http_server_properties_dict;
+ base::DictionaryValue* servers_dict = new base::DictionaryValue;
+ for (ServerPrefMap::const_iterator map_it = server_pref_map.begin();
+ map_it != server_pref_map.end();
+ ++map_it) {
+ const net::HostPortPair& server = map_it->first;
+ const ServerPref& server_pref = map_it->second;
+
+ base::DictionaryValue* server_pref_dict = new base::DictionaryValue;
+
+ // Save supports_spdy.
+ if (server_pref.supports_spdy)
+ server_pref_dict->SetBoolean("supports_spdy", server_pref.supports_spdy);
+
+ // Save SPDY settings.
+ if (server_pref.settings_map) {
+ base::DictionaryValue* spdy_settings_dict = new base::DictionaryValue;
+ for (net::SettingsMap::const_iterator it =
+ server_pref.settings_map->begin();
+ it != server_pref.settings_map->end();
+ ++it) {
+ net::SpdySettingsIds id = it->first;
+ uint32 value = it->second.second;
+ std::string key = base::StringPrintf("%u", id);
+ spdy_settings_dict->SetInteger(key, value);
+ }
+ server_pref_dict->SetWithoutPathExpansion("settings", spdy_settings_dict);
+ }
+
+ // Save alternate_protocol.
+ if (server_pref.alternate_protocol) {
+ base::DictionaryValue* port_alternate_protocol_dict =
+ new base::DictionaryValue;
+ const net::AlternateProtocolInfo* port_alternate_protocol =
+ server_pref.alternate_protocol;
+ port_alternate_protocol_dict->SetInteger("port",
+ port_alternate_protocol->port);
+ const char* protocol_str =
+ net::AlternateProtocolToString(port_alternate_protocol->protocol);
+ port_alternate_protocol_dict->SetString("protocol_str", protocol_str);
+ port_alternate_protocol_dict->SetDouble(
+ "probability", port_alternate_protocol->probability);
+ server_pref_dict->SetWithoutPathExpansion(
+ "alternate_protocol", port_alternate_protocol_dict);
+ }
+
+ servers_dict->SetWithoutPathExpansion(server.ToString(), server_pref_dict);
+ }
+
+ http_server_properties_dict.SetWithoutPathExpansion("servers", servers_dict);
+ SetVersion(&http_server_properties_dict, kVersionNumber);
+ setting_prefs_ = true;
+ pref_service_->Set(path_, http_server_properties_dict);
+ setting_prefs_ = false;
+
+ // Note that |completion| will be fired after we have written everything to
+ // the Preferences, but likely before these changes are serialized to disk.
+ // This is not a problem though, as JSONPrefStore guarantees that this will
+ // happen, pretty soon, and even in the case we shut down immediately.
+ if (!completion.is_null())
+ completion.Run();
+}
+
+void HttpServerPropertiesManager::OnHttpServerPropertiesChanged() {
+ DCHECK(pref_task_runner_->RunsTasksOnCurrentThread());
+ if (!setting_prefs_)
+ ScheduleUpdateCacheOnPrefThread();
+}
+
+} // namespace net
diff --git a/net/http/http_server_properties_manager.h b/net/http/http_server_properties_manager.h
new file mode 100644
index 0000000..931b12e
--- /dev/null
+++ b/net/http/http_server_properties_manager.h
@@ -0,0 +1,263 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef NET_HTTP_HTTP_SERVER_PROPERTIES_MANAGER_H_
+#define NET_HTTP_HTTP_SERVER_PROPERTIES_MANAGER_H_
+
+#include <string>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/prefs/pref_change_registrar.h"
+#include "base/timer/timer.h"
+#include "base/values.h"
+#include "net/base/host_port_pair.h"
+#include "net/http/http_server_properties.h"
+#include "net/http/http_server_properties_impl.h"
+
+class PrefService;
+
+namespace base {
+class SequencedTaskRunner;
+}
+
+namespace net {
+
+////////////////////////////////////////////////////////////////////////////////
+// HttpServerPropertiesManager
+
+// The manager for creating and updating an HttpServerProperties (for example it
+// tracks if a server supports SPDY or not).
+//
+// This class interacts with both the pref thread, where notifications of pref
+// changes are received from, and the network thread, which owns it, and it
+// persists the changes from network stack whether server supports SPDY or not.
+//
+// It must be constructed on the pref thread, to set up |pref_task_runner_| and
+// the prefs listeners.
+//
+// ShutdownOnPrefThread must be called from pref thread before destruction, to
+// release the prefs listeners on the pref thread.
+//
+// Class requires that update tasks from the Pref thread can post safely to the
+// network thread, so the destruction order must guarantee that if |this|
+// exists in pref thread, then a potential destruction on network thread will
+// come after any task posted to network thread from that method on pref thread.
+// This is used to go through network thread before the actual update starts,
+// and grab a WeakPtr.
+class NET_EXPORT HttpServerPropertiesManager : public HttpServerProperties {
+ public:
+ // Create an instance of the HttpServerPropertiesManager. The lifetime of the
+ // PrefService objects must be longer than that of the
+ // HttpServerPropertiesManager object. Must be constructed on the Pref thread.
+ HttpServerPropertiesManager(
+ PrefService* pref_service,
+ const char* pref_path,
+ scoped_refptr<base::SequencedTaskRunner> network_task_runner);
+ virtual ~HttpServerPropertiesManager();
+
+ // Initialize on Network thread.
+ void InitializeOnNetworkThread();
+
+ // Prepare for shutdown. Must be called on the Pref thread before destruction.
+ void ShutdownOnPrefThread();
+
+ // Helper function for unit tests to set the version in the dictionary.
+ static void SetVersion(base::DictionaryValue* http_server_properties_dict,
+ int version_number);
+
+ // Deletes all data. Works asynchronously, but if a |completion| callback is
+ // provided, it will be fired on the pref thread when everything is done.
+ void Clear(const base::Closure& completion);
+
+ // ----------------------------------
+ // HttpServerProperties methods:
+ // ----------------------------------
+
+ // Gets a weak pointer for this object.
+ virtual base::WeakPtr<HttpServerProperties> GetWeakPtr() OVERRIDE;
+
+ // Deletes all data. Works asynchronously.
+ virtual void Clear() OVERRIDE;
+
+ // Returns true if |server| supports SPDY. Should only be called from IO
+ // thread.
+ virtual bool SupportsSpdy(const HostPortPair& server) OVERRIDE;
+
+ // Add |server| as the SPDY server which supports SPDY protocol into the
+ // persisitent store. Should only be called from IO thread.
+ virtual void SetSupportsSpdy(const HostPortPair& server,
+ bool support_spdy) OVERRIDE;
+
+ // Returns true if |server| has an Alternate-Protocol header.
+ virtual bool HasAlternateProtocol(const HostPortPair& server) OVERRIDE;
+
+ // Returns the Alternate-Protocol and port for |server|.
+ // HasAlternateProtocol(server) must be true.
+ virtual AlternateProtocolInfo GetAlternateProtocol(
+ const HostPortPair& server) OVERRIDE;
+
+ // Sets the Alternate-Protocol for |server|.
+ virtual void SetAlternateProtocol(
+ const HostPortPair& server,
+ uint16 alternate_port,
+ AlternateProtocol alternate_protocol,
+ double alternate_probability) OVERRIDE;
+
+ // Sets the Alternate-Protocol for |server| to be BROKEN.
+ virtual void SetBrokenAlternateProtocol(const HostPortPair& server) OVERRIDE;
+
+ // Returns true if Alternate-Protocol for |server| was recently BROKEN.
+ virtual bool WasAlternateProtocolRecentlyBroken(
+ const HostPortPair& server) OVERRIDE;
+
+ // Confirms that Alternate-Protocol for |server| is working.
+ virtual void ConfirmAlternateProtocol(const HostPortPair& server) OVERRIDE;
+
+ // Clears the Alternate-Protocol for |server|.
+ virtual void ClearAlternateProtocol(const HostPortPair& server) OVERRIDE;
+
+ // Returns all Alternate-Protocol mappings.
+ virtual const AlternateProtocolMap& alternate_protocol_map() const OVERRIDE;
+
+ virtual void SetAlternateProtocolExperiment(
+ AlternateProtocolExperiment experiment) OVERRIDE;
+
+ virtual void SetAlternateProtocolProbabilityThreshold(
+ double threshold) OVERRIDE;
+
+ virtual AlternateProtocolExperiment GetAlternateProtocolExperiment()
+ const OVERRIDE;
+
+ // Gets a reference to the SettingsMap stored for a host.
+ // If no settings are stored, returns an empty SettingsMap.
+ virtual const SettingsMap& GetSpdySettings(
+ const HostPortPair& host_port_pair) OVERRIDE;
+
+ // Saves an individual SPDY setting for a host. Returns true if SPDY setting
+ // is to be persisted.
+ virtual bool SetSpdySetting(const HostPortPair& host_port_pair,
+ SpdySettingsIds id,
+ SpdySettingsFlags flags,
+ uint32 value) OVERRIDE;
+
+ // Clears all SPDY settings for a host.
+ virtual void ClearSpdySettings(const HostPortPair& host_port_pair) OVERRIDE;
+
+ // Clears all SPDY settings for all hosts.
+ virtual void ClearAllSpdySettings() OVERRIDE;
+
+ // Returns all SPDY persistent settings.
+ virtual const SpdySettingsMap& spdy_settings_map() const OVERRIDE;
+
+ virtual void SetServerNetworkStats(const HostPortPair& host_port_pair,
+ NetworkStats stats) OVERRIDE;
+
+ virtual const NetworkStats* GetServerNetworkStats(
+ const HostPortPair& host_port_pair) const OVERRIDE;
+
+ protected:
+ // --------------------
+ // SPDY related methods
+
+ // These are used to delay updating of the cached data in
+ // |http_server_properties_impl_| while the preferences are changing, and
+ // execute only one update per simultaneous prefs changes.
+ void ScheduleUpdateCacheOnPrefThread();
+
+ // Starts the timers to update the cached prefs. This are overridden in tests
+ // to prevent the delay.
+ virtual void StartCacheUpdateTimerOnPrefThread(base::TimeDelta delay);
+
+ // Update cached prefs in |http_server_properties_impl_| with data from
+ // preferences. It gets the data on pref thread and calls
+ // UpdateSpdyServersFromPrefsOnNetworkThread() to perform the update on
+ // network thread.
+ virtual void UpdateCacheFromPrefsOnPrefThread();
+
+ // Starts the update of cached prefs in |http_server_properties_impl_| on the
+ // network thread. Protected for testing.
+ void UpdateCacheFromPrefsOnNetworkThread(
+ std::vector<std::string>* spdy_servers,
+ SpdySettingsMap* spdy_settings_map,
+ AlternateProtocolMap* alternate_protocol_map,
+ AlternateProtocolExperiment alternate_protocol_experiment,
+ bool detected_corrupted_prefs);
+
+ // These are used to delay updating the preferences when cached data in
+ // |http_server_properties_impl_| is changing, and execute only one update per
+ // simultaneous spdy_servers or spdy_settings or alternate_protocol changes.
+ void ScheduleUpdatePrefsOnNetworkThread();
+
+ // Starts the timers to update the prefs from cache. This are overridden in
+ // tests to prevent the delay.
+ virtual void StartPrefsUpdateTimerOnNetworkThread(base::TimeDelta delay);
+
+ // Update prefs::kHttpServerProperties in preferences with the cached data
+ // from |http_server_properties_impl_|. This gets the data on network thread
+ // and posts a task (UpdatePrefsOnPrefThread) to update preferences on pref
+ // thread.
+ void UpdatePrefsFromCacheOnNetworkThread();
+
+ // Same as above, but fires an optional |completion| callback on pref thread
+ // when finished. Virtual for testing.
+ virtual void UpdatePrefsFromCacheOnNetworkThread(
+ const base::Closure& completion);
+
+ // Update prefs::kHttpServerProperties preferences on pref thread. Executes an
+ // optional |completion| callback when finished. Protected for testing.
+ void UpdatePrefsOnPrefThread(base::ListValue* spdy_server_list,
+ SpdySettingsMap* spdy_settings_map,
+ AlternateProtocolMap* alternate_protocol_map,
+ const base::Closure& completion);
+
+ private:
+ void OnHttpServerPropertiesChanged();
+
+ // -----------
+ // Pref thread
+ // -----------
+
+ const scoped_refptr<base::SequencedTaskRunner> pref_task_runner_;
+
+ // Used to get |weak_ptr_| to self on the pref thread.
+ scoped_ptr<base::WeakPtrFactory<HttpServerPropertiesManager> >
+ pref_weak_ptr_factory_;
+
+ base::WeakPtr<HttpServerPropertiesManager> pref_weak_ptr_;
+
+ // Used to post cache update tasks.
+ scoped_ptr<base::OneShotTimer<HttpServerPropertiesManager> >
+ pref_cache_update_timer_;
+
+ // Used to track the spdy servers changes.
+ PrefChangeRegistrar pref_change_registrar_;
+ PrefService* pref_service_; // Weak.
+ bool setting_prefs_;
+ const char* path_;
+
+ // --------------
+ // Network thread
+ // --------------
+
+ const scoped_refptr<base::SequencedTaskRunner> network_task_runner_;
+
+ // Used to get |weak_ptr_| to self on the network thread.
+ scoped_ptr<base::WeakPtrFactory<HttpServerPropertiesManager> >
+ network_weak_ptr_factory_;
+
+ // Used to post |prefs::kHttpServerProperties| pref update tasks.
+ scoped_ptr<base::OneShotTimer<HttpServerPropertiesManager> >
+ network_prefs_update_timer_;
+
+ scoped_ptr<HttpServerPropertiesImpl> http_server_properties_impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpServerPropertiesManager);
+};
+
+} // namespace net
+
+#endif // NET_HTTP_HTTP_SERVER_PROPERTIES_MANAGER_H_
diff --git a/net/http/http_server_properties_manager_unittest.cc b/net/http/http_server_properties_manager_unittest.cc
new file mode 100644
index 0000000..ad2a08b
--- /dev/null
+++ b/net/http/http_server_properties_manager_unittest.cc
@@ -0,0 +1,484 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http/http_server_properties_manager.h"
+
+#include "base/basictypes.h"
+#include "base/message_loop/message_loop.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/testing_pref_service.h"
+#include "base/run_loop.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/values.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace net {
+
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Mock;
+using ::testing::StrictMock;
+
+const char kTestHttpServerProperties[] = "TestHttpServerProperties";
+
+class TestingHttpServerPropertiesManager : public HttpServerPropertiesManager {
+ public:
+ TestingHttpServerPropertiesManager(
+ PrefService* pref_service,
+ const char* pref_path,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+ : HttpServerPropertiesManager(pref_service, pref_path, io_task_runner) {
+ InitializeOnNetworkThread();
+ }
+
+ virtual ~TestingHttpServerPropertiesManager() {}
+
+ // Make these methods public for testing.
+ using HttpServerPropertiesManager::ScheduleUpdateCacheOnPrefThread;
+ using HttpServerPropertiesManager::ScheduleUpdatePrefsOnNetworkThread;
+
+ // Post tasks without a delay during tests.
+ virtual void StartPrefsUpdateTimerOnNetworkThread(
+ base::TimeDelta delay) OVERRIDE {
+ HttpServerPropertiesManager::StartPrefsUpdateTimerOnNetworkThread(
+ base::TimeDelta());
+ }
+
+ void UpdateCacheFromPrefsOnUIConcrete() {
+ HttpServerPropertiesManager::UpdateCacheFromPrefsOnPrefThread();
+ }
+
+ // Post tasks without a delay during tests.
+ virtual void StartCacheUpdateTimerOnPrefThread(
+ base::TimeDelta delay) OVERRIDE {
+ HttpServerPropertiesManager::StartCacheUpdateTimerOnPrefThread(
+ base::TimeDelta());
+ }
+
+ void UpdatePrefsFromCacheOnNetworkThreadConcrete(
+ const base::Closure& callback) {
+ HttpServerPropertiesManager::UpdatePrefsFromCacheOnNetworkThread(callback);
+ }
+
+ MOCK_METHOD0(UpdateCacheFromPrefsOnPrefThread, void());
+ MOCK_METHOD1(UpdatePrefsFromCacheOnNetworkThread, void(const base::Closure&));
+ MOCK_METHOD5(UpdateCacheFromPrefsOnNetworkThread,
+ void(std::vector<std::string>* spdy_servers,
+ net::SpdySettingsMap* spdy_settings_map,
+ net::AlternateProtocolMap* alternate_protocol_map,
+ net::AlternateProtocolExperiment experiment,
+ bool detected_corrupted_prefs));
+ MOCK_METHOD3(UpdatePrefsOnPref,
+ void(base::ListValue* spdy_server_list,
+ net::SpdySettingsMap* spdy_settings_map,
+ net::AlternateProtocolMap* alternate_protocol_map));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestingHttpServerPropertiesManager);
+};
+
+class HttpServerPropertiesManagerTest : public testing::Test {
+ protected:
+ HttpServerPropertiesManagerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ pref_service_.registry()->RegisterDictionaryPref(kTestHttpServerProperties);
+ http_server_props_manager_.reset(
+ new StrictMock<TestingHttpServerPropertiesManager>(
+ &pref_service_,
+ kTestHttpServerProperties,
+ base::MessageLoop::current()->message_loop_proxy()));
+ ExpectCacheUpdate();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ virtual void TearDown() OVERRIDE {
+ if (http_server_props_manager_.get())
+ http_server_props_manager_->ShutdownOnPrefThread();
+ base::RunLoop().RunUntilIdle();
+ http_server_props_manager_.reset();
+ }
+
+ void ExpectCacheUpdate() {
+ EXPECT_CALL(*http_server_props_manager_, UpdateCacheFromPrefsOnPrefThread())
+ .WillOnce(Invoke(http_server_props_manager_.get(),
+ &TestingHttpServerPropertiesManager::
+ UpdateCacheFromPrefsOnUIConcrete));
+ }
+
+ void ExpectPrefsUpdate() {
+ EXPECT_CALL(*http_server_props_manager_,
+ UpdatePrefsFromCacheOnNetworkThread(_))
+ .WillOnce(Invoke(http_server_props_manager_.get(),
+ &TestingHttpServerPropertiesManager::
+ UpdatePrefsFromCacheOnNetworkThreadConcrete));
+ }
+
+ void ExpectPrefsUpdateRepeatedly() {
+ EXPECT_CALL(*http_server_props_manager_,
+ UpdatePrefsFromCacheOnNetworkThread(_))
+ .WillRepeatedly(
+ Invoke(http_server_props_manager_.get(),
+ &TestingHttpServerPropertiesManager::
+ UpdatePrefsFromCacheOnNetworkThreadConcrete));
+ }
+
+ //base::RunLoop loop_;
+ TestingPrefServiceSimple pref_service_;
+ scoped_ptr<TestingHttpServerPropertiesManager> http_server_props_manager_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HttpServerPropertiesManagerTest);
+};
+
+TEST_F(HttpServerPropertiesManagerTest,
+ SingleUpdateForTwoSpdyServerPrefChanges) {
+ ExpectCacheUpdate();
+
+ // Set up the prefs for www.google.com:80 and mail.google.com:80 and then set
+ // it twice. Only expect a single cache update.
+
+ base::DictionaryValue* server_pref_dict = new base::DictionaryValue;
+
+ // Set supports_spdy for www.google.com:80.
+ server_pref_dict->SetBoolean("supports_spdy", true);
+
+ // Set up alternate_protocol for www.google.com:80.
+ base::DictionaryValue* alternate_protocol = new base::DictionaryValue;
+ alternate_protocol->SetInteger("port", 443);
+ alternate_protocol->SetString("protocol_str", "npn-spdy/3");
+ server_pref_dict->SetWithoutPathExpansion("alternate_protocol",
+ alternate_protocol);
+
+ // Set the server preference for www.google.com:80.
+ base::DictionaryValue* servers_dict = new base::DictionaryValue;
+ servers_dict->SetWithoutPathExpansion("www.google.com:80", server_pref_dict);
+
+ // Set the preference for mail.google.com server.
+ base::DictionaryValue* server_pref_dict1 = new base::DictionaryValue;
+
+ // Set supports_spdy for mail.google.com:80
+ server_pref_dict1->SetBoolean("supports_spdy", true);
+
+ // Set up alternate_protocol for mail.google.com:80
+ base::DictionaryValue* alternate_protocol1 = new base::DictionaryValue;
+ alternate_protocol1->SetInteger("port", 444);
+ alternate_protocol1->SetString("protocol_str", "npn-spdy/3.1");
+
+ server_pref_dict1->SetWithoutPathExpansion("alternate_protocol",
+ alternate_protocol1);
+
+ // Set the server preference for mail.google.com:80.
+ servers_dict->SetWithoutPathExpansion("mail.google.com:80",
+ server_pref_dict1);
+
+ base::DictionaryValue* http_server_properties_dict =
+ new base::DictionaryValue;
+ HttpServerPropertiesManager::SetVersion(http_server_properties_dict, -1);
+ http_server_properties_dict->SetWithoutPathExpansion("servers", servers_dict);
+
+ // Set the same value for kHttpServerProperties multiple times.
+ pref_service_.SetManagedPref(kTestHttpServerProperties,
+ http_server_properties_dict);
+ base::DictionaryValue* http_server_properties_dict2 =
+ http_server_properties_dict->DeepCopy();
+ pref_service_.SetManagedPref(kTestHttpServerProperties,
+ http_server_properties_dict2);
+
+ base::RunLoop().RunUntilIdle();
+ Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
+
+ // Verify SupportsSpdy.
+ EXPECT_TRUE(http_server_props_manager_->SupportsSpdy(
+ net::HostPortPair::FromString("www.google.com:80")));
+ EXPECT_TRUE(http_server_props_manager_->SupportsSpdy(
+ net::HostPortPair::FromString("mail.google.com:80")));
+ EXPECT_FALSE(http_server_props_manager_->SupportsSpdy(
+ net::HostPortPair::FromString("foo.google.com:1337")));
+
+ // Verify AlternateProtocol.
+ ASSERT_TRUE(http_server_props_manager_->HasAlternateProtocol(
+ net::HostPortPair::FromString("www.google.com:80")));
+ ASSERT_TRUE(http_server_props_manager_->HasAlternateProtocol(
+ net::HostPortPair::FromString("mail.google.com:80")));
+ net::AlternateProtocolInfo port_alternate_protocol =
+ http_server_props_manager_->GetAlternateProtocol(
+ net::HostPortPair::FromString("www.google.com:80"));
+ EXPECT_EQ(443, port_alternate_protocol.port);
+ EXPECT_EQ(net::NPN_SPDY_3, port_alternate_protocol.protocol);
+ port_alternate_protocol = http_server_props_manager_->GetAlternateProtocol(
+ net::HostPortPair::FromString("mail.google.com:80"));
+ EXPECT_EQ(444, port_alternate_protocol.port);
+ EXPECT_EQ(net::NPN_SPDY_3_1, port_alternate_protocol.protocol);
+}
+
+TEST_F(HttpServerPropertiesManagerTest, SupportsSpdy) {
+ ExpectPrefsUpdate();
+
+ // Post an update task to the network thread. SetSupportsSpdy calls
+ // ScheduleUpdatePrefsOnNetworkThread.
+
+ // Add mail.google.com:443 as a supporting spdy server.
+ net::HostPortPair spdy_server_mail("mail.google.com", 443);
+ EXPECT_FALSE(http_server_props_manager_->SupportsSpdy(spdy_server_mail));
+ http_server_props_manager_->SetSupportsSpdy(spdy_server_mail, true);
+
+ // Run the task.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(http_server_props_manager_->SupportsSpdy(spdy_server_mail));
+ Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
+}
+
+TEST_F(HttpServerPropertiesManagerTest, SetSpdySetting) {
+ ExpectPrefsUpdate();
+
+ // Add SpdySetting for mail.google.com:443.
+ net::HostPortPair spdy_server_mail("mail.google.com", 443);
+ const net::SpdySettingsIds id1 = net::SETTINGS_UPLOAD_BANDWIDTH;
+ const net::SpdySettingsFlags flags1 = net::SETTINGS_FLAG_PLEASE_PERSIST;
+ const uint32 value1 = 31337;
+ http_server_props_manager_->SetSpdySetting(
+ spdy_server_mail, id1, flags1, value1);
+
+ // Run the task.
+ base::RunLoop().RunUntilIdle();
+
+ const net::SettingsMap& settings_map1_ret =
+ http_server_props_manager_->GetSpdySettings(spdy_server_mail);
+ ASSERT_EQ(1U, settings_map1_ret.size());
+ net::SettingsMap::const_iterator it1_ret = settings_map1_ret.find(id1);
+ EXPECT_TRUE(it1_ret != settings_map1_ret.end());
+ net::SettingsFlagsAndValue flags_and_value1_ret = it1_ret->second;
+ EXPECT_EQ(net::SETTINGS_FLAG_PERSISTED, flags_and_value1_ret.first);
+ EXPECT_EQ(value1, flags_and_value1_ret.second);
+
+ Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
+}
+
+TEST_F(HttpServerPropertiesManagerTest, ClearSpdySetting) {
+ ExpectPrefsUpdateRepeatedly();
+
+ // Add SpdySetting for mail.google.com:443.
+ net::HostPortPair spdy_server_mail("mail.google.com", 443);
+ const net::SpdySettingsIds id1 = net::SETTINGS_UPLOAD_BANDWIDTH;
+ const net::SpdySettingsFlags flags1 = net::SETTINGS_FLAG_PLEASE_PERSIST;
+ const uint32 value1 = 31337;
+ http_server_props_manager_->SetSpdySetting(
+ spdy_server_mail, id1, flags1, value1);
+
+ // Run the task.
+ base::RunLoop().RunUntilIdle();
+
+ const net::SettingsMap& settings_map1_ret =
+ http_server_props_manager_->GetSpdySettings(spdy_server_mail);
+ ASSERT_EQ(1U, settings_map1_ret.size());
+ net::SettingsMap::const_iterator it1_ret = settings_map1_ret.find(id1);
+ EXPECT_TRUE(it1_ret != settings_map1_ret.end());
+ net::SettingsFlagsAndValue flags_and_value1_ret = it1_ret->second;
+ EXPECT_EQ(net::SETTINGS_FLAG_PERSISTED, flags_and_value1_ret.first);
+ EXPECT_EQ(value1, flags_and_value1_ret.second);
+
+ // Clear SpdySetting for mail.google.com:443.
+ http_server_props_manager_->ClearSpdySettings(spdy_server_mail);
+
+ // Run the task.
+ base::RunLoop().RunUntilIdle();
+
+ // Verify that there are no entries in the settings map for
+ // mail.google.com:443.
+ const net::SettingsMap& settings_map2_ret =
+ http_server_props_manager_->GetSpdySettings(spdy_server_mail);
+ ASSERT_EQ(0U, settings_map2_ret.size());
+
+ Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
+}
+
+TEST_F(HttpServerPropertiesManagerTest, ClearAllSpdySetting) {
+ ExpectPrefsUpdateRepeatedly();
+
+ // Add SpdySetting for mail.google.com:443.
+ net::HostPortPair spdy_server_mail("mail.google.com", 443);
+ const net::SpdySettingsIds id1 = net::SETTINGS_UPLOAD_BANDWIDTH;
+ const net::SpdySettingsFlags flags1 = net::SETTINGS_FLAG_PLEASE_PERSIST;
+ const uint32 value1 = 31337;
+ http_server_props_manager_->SetSpdySetting(
+ spdy_server_mail, id1, flags1, value1);
+
+ // Run the task.
+ base::RunLoop().RunUntilIdle();
+
+ const net::SettingsMap& settings_map1_ret =
+ http_server_props_manager_->GetSpdySettings(spdy_server_mail);
+ ASSERT_EQ(1U, settings_map1_ret.size());
+ net::SettingsMap::const_iterator it1_ret = settings_map1_ret.find(id1);
+ EXPECT_TRUE(it1_ret != settings_map1_ret.end());
+ net::SettingsFlagsAndValue flags_and_value1_ret = it1_ret->second;
+ EXPECT_EQ(net::SETTINGS_FLAG_PERSISTED, flags_and_value1_ret.first);
+ EXPECT_EQ(value1, flags_and_value1_ret.second);
+
+ // Clear All SpdySettings.
+ http_server_props_manager_->ClearAllSpdySettings();
+
+ // Run the task.
+ base::RunLoop().RunUntilIdle();
+
+ // Verify that there are no entries in the settings map.
+ const net::SpdySettingsMap& spdy_settings_map2_ret =
+ http_server_props_manager_->spdy_settings_map();
+ ASSERT_EQ(0U, spdy_settings_map2_ret.size());
+
+ Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
+}
+
+TEST_F(HttpServerPropertiesManagerTest, HasAlternateProtocol) {
+ ExpectPrefsUpdate();
+
+ net::HostPortPair spdy_server_mail("mail.google.com", 80);
+ EXPECT_FALSE(
+ http_server_props_manager_->HasAlternateProtocol(spdy_server_mail));
+ http_server_props_manager_->SetAlternateProtocol(
+ spdy_server_mail, 443, net::NPN_SPDY_3, 1);
+
+ // Run the task.
+ base::RunLoop().RunUntilIdle();
+ Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
+
+ ASSERT_TRUE(
+ http_server_props_manager_->HasAlternateProtocol(spdy_server_mail));
+ net::AlternateProtocolInfo port_alternate_protocol =
+ http_server_props_manager_->GetAlternateProtocol(spdy_server_mail);
+ EXPECT_EQ(443, port_alternate_protocol.port);
+ EXPECT_EQ(net::NPN_SPDY_3, port_alternate_protocol.protocol);
+}
+
+TEST_F(HttpServerPropertiesManagerTest, Clear) {
+ ExpectPrefsUpdate();
+
+ net::HostPortPair spdy_server_mail("mail.google.com", 443);
+ http_server_props_manager_->SetSupportsSpdy(spdy_server_mail, true);
+ http_server_props_manager_->SetAlternateProtocol(
+ spdy_server_mail, 443, net::NPN_SPDY_3, 1);
+
+ const net::SpdySettingsIds id1 = net::SETTINGS_UPLOAD_BANDWIDTH;
+ const net::SpdySettingsFlags flags1 = net::SETTINGS_FLAG_PLEASE_PERSIST;
+ const uint32 value1 = 31337;
+ http_server_props_manager_->SetSpdySetting(
+ spdy_server_mail, id1, flags1, value1);
+
+ // Run the task.
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(http_server_props_manager_->SupportsSpdy(spdy_server_mail));
+ EXPECT_TRUE(
+ http_server_props_manager_->HasAlternateProtocol(spdy_server_mail));
+
+ // Check SPDY settings values.
+ const net::SettingsMap& settings_map1_ret =
+ http_server_props_manager_->GetSpdySettings(spdy_server_mail);
+ ASSERT_EQ(1U, settings_map1_ret.size());
+ net::SettingsMap::const_iterator it1_ret = settings_map1_ret.find(id1);
+ EXPECT_TRUE(it1_ret != settings_map1_ret.end());
+ net::SettingsFlagsAndValue flags_and_value1_ret = it1_ret->second;
+ EXPECT_EQ(net::SETTINGS_FLAG_PERSISTED, flags_and_value1_ret.first);
+ EXPECT_EQ(value1, flags_and_value1_ret.second);
+
+ Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
+
+ ExpectPrefsUpdate();
+
+ // Clear http server data, time out if we do not get a completion callback.
+ http_server_props_manager_->Clear(base::MessageLoop::QuitClosure());
+ base::RunLoop().Run();
+
+ EXPECT_FALSE(http_server_props_manager_->SupportsSpdy(spdy_server_mail));
+ EXPECT_FALSE(
+ http_server_props_manager_->HasAlternateProtocol(spdy_server_mail));
+
+ const net::SettingsMap& settings_map2_ret =
+ http_server_props_manager_->GetSpdySettings(spdy_server_mail);
+ EXPECT_EQ(0U, settings_map2_ret.size());
+
+ Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
+}
+
+TEST_F(HttpServerPropertiesManagerTest, ShutdownWithPendingUpdateCache0) {
+ // Post an update task to the UI thread.
+ http_server_props_manager_->ScheduleUpdateCacheOnPrefThread();
+ // Shutdown comes before the task is executed.
+ http_server_props_manager_->ShutdownOnPrefThread();
+ http_server_props_manager_.reset();
+ // Run the task after shutdown and deletion.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(HttpServerPropertiesManagerTest, ShutdownWithPendingUpdateCache1) {
+ // Post an update task.
+ http_server_props_manager_->ScheduleUpdateCacheOnPrefThread();
+ // Shutdown comes before the task is executed.
+ http_server_props_manager_->ShutdownOnPrefThread();
+ // Run the task after shutdown, but before deletion.
+ base::RunLoop().RunUntilIdle();
+ Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
+ http_server_props_manager_.reset();
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(HttpServerPropertiesManagerTest, ShutdownWithPendingUpdateCache2) {
+ http_server_props_manager_->UpdateCacheFromPrefsOnUIConcrete();
+ // Shutdown comes before the task is executed.
+ http_server_props_manager_->ShutdownOnPrefThread();
+ // Run the task after shutdown, but before deletion.
+ base::RunLoop().RunUntilIdle();
+ Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
+ http_server_props_manager_.reset();
+ base::RunLoop().RunUntilIdle();
+}
+
+//
+// Tests for shutdown when updating prefs.
+//
+TEST_F(HttpServerPropertiesManagerTest, ShutdownWithPendingUpdatePrefs0) {
+ // Post an update task to the IO thread.
+ http_server_props_manager_->ScheduleUpdatePrefsOnNetworkThread();
+ // Shutdown comes before the task is executed.
+ http_server_props_manager_->ShutdownOnPrefThread();
+ http_server_props_manager_.reset();
+ // Run the task after shutdown and deletion.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(HttpServerPropertiesManagerTest, ShutdownWithPendingUpdatePrefs1) {
+ ExpectPrefsUpdate();
+ // Post an update task.
+ http_server_props_manager_->ScheduleUpdatePrefsOnNetworkThread();
+ // Shutdown comes before the task is executed.
+ http_server_props_manager_->ShutdownOnPrefThread();
+ // Run the task after shutdown, but before deletion.
+ base::RunLoop().RunUntilIdle();
+ Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
+ http_server_props_manager_.reset();
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(HttpServerPropertiesManagerTest, ShutdownWithPendingUpdatePrefs2) {
+ // This posts a task to the UI thread.
+ http_server_props_manager_->UpdatePrefsFromCacheOnNetworkThreadConcrete(
+ base::Closure());
+ // Shutdown comes before the task is executed.
+ http_server_props_manager_->ShutdownOnPrefThread();
+ // Run the task after shutdown, but before deletion.
+ base::RunLoop().RunUntilIdle();
+ Mock::VerifyAndClearExpectations(http_server_props_manager_.get());
+ http_server_props_manager_.reset();
+ base::RunLoop().RunUntilIdle();
+}
+
+} // namespace
+
+} // namespace net