diff options
author | mef@chromium.org <mef@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-10 12:43:47 +0000 |
---|---|---|
committer | mef@chromium.org <mef@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-10 12:43:47 +0000 |
commit | ad66db1afee6c9be75a7de80901fd774b6cf027b (patch) | |
tree | 16c32dd9f737919dfe1883d422b4c7add11eb65d /net/http | |
parent | 8ca02c0ba49bd0e7ee1fc7a9619dce8fa72fbaea (diff) | |
download | chromium_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.cc | 734 | ||||
-rw-r--r-- | net/http/http_server_properties_manager.h | 263 | ||||
-rw-r--r-- | net/http/http_server_properties_manager_unittest.cc | 484 |
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 |