// 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_macros.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" #include "net/base/ip_address.h" #include "net/base/port_util.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_t 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_t kUpdatePrefsDelayMs = 60000; // "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 = 4; // Persist 200 MRU AlternateProtocolHostPortPairs. const int kMaxAlternateProtocolHostsToPersist = 200; // Persist 200 MRU SpdySettingsHostPortPairs. const int kMaxSpdySettingsHostsToPersist = 200; // Persist 300 MRU SupportsSpdyServerHostPortPairs. const int kMaxSupportsSpdyServerHostsToPersist = 300; // Persist 200 ServerNetworkStats. const int kMaxServerNetworkStatsHostsToPersist = 200; const char kVersionKey[] = "version"; const char kServersKey[] = "servers"; const char kSupportsSpdyKey[] = "supports_spdy"; const char kSettingsKey[] = "settings"; const char kSupportsQuicKey[] = "supports_quic"; const char kQuicServers[] = "quic_servers"; const char kServerInfoKey[] = "server_info"; const char kUsedQuicKey[] = "used_quic"; const char kAddressKey[] = "address"; const char kAlternativeServiceKey[] = "alternative_service"; const char kProtocolKey[] = "protocol_str"; const char kHostKey[] = "host"; const char kPortKey[] = "port"; const char kExpirationKey[] = "expiration"; const char kNetworkStatsKey[] = "network_stats"; const char kSrttKey[] = "srtt"; } // namespace //////////////////////////////////////////////////////////////////////////////// // HttpServerPropertiesManager HttpServerPropertiesManager::PrefDelegate::~PrefDelegate() {} HttpServerPropertiesManager::HttpServerPropertiesManager( PrefDelegate* pref_delegate, scoped_refptr network_task_runner) : pref_task_runner_(base::ThreadTaskRunnerHandle::Get()), pref_delegate_(pref_delegate), setting_prefs_(false), network_task_runner_(network_task_runner) { DCHECK(pref_delegate_); pref_weak_ptr_factory_.reset( new base::WeakPtrFactory(this)); pref_weak_ptr_ = pref_weak_ptr_factory_->GetWeakPtr(); pref_cache_update_timer_.reset(new base::OneShotTimer); pref_delegate_->StartListeningForUpdates( 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(this)); http_server_properties_impl_.reset(new HttpServerPropertiesImpl()); network_prefs_update_timer_.reset(new base::OneShotTimer); 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_delegate_->StopListeningForUpdates(); } // 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(kVersionKey, version_number); } // This is required for conformance with the HttpServerProperties interface. base::WeakPtr 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::SupportsRequestPriority( const HostPortPair& server) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->SupportsRequestPriority(server); } bool HttpServerPropertiesManager::GetSupportsSpdy(const HostPortPair& server) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->GetSupportsSpdy(server); } void HttpServerPropertiesManager::SetSupportsSpdy(const HostPortPair& server, bool support_spdy) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); bool old_support_spdy = http_server_properties_impl_->GetSupportsSpdy(server); http_server_properties_impl_->SetSupportsSpdy(server, support_spdy); bool new_support_spdy = http_server_properties_impl_->GetSupportsSpdy(server); if (old_support_spdy != new_support_spdy) ScheduleUpdatePrefsOnNetworkThread(SUPPORTS_SPDY); } bool HttpServerPropertiesManager::RequiresHTTP11(const HostPortPair& server) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->RequiresHTTP11(server); } void HttpServerPropertiesManager::SetHTTP11Required( const HostPortPair& server) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); http_server_properties_impl_->SetHTTP11Required(server); ScheduleUpdatePrefsOnNetworkThread(HTTP_11_REQUIRED); } void HttpServerPropertiesManager::MaybeForceHTTP11(const HostPortPair& server, SSLConfig* ssl_config) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); http_server_properties_impl_->MaybeForceHTTP11(server, ssl_config); } AlternativeServiceVector HttpServerPropertiesManager::GetAlternativeServices( const HostPortPair& origin) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->GetAlternativeServices(origin); } bool HttpServerPropertiesManager::SetAlternativeService( const HostPortPair& origin, const AlternativeService& alternative_service, base::Time expiration) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); const bool changed = http_server_properties_impl_->SetAlternativeService( origin, alternative_service, expiration); if (changed) { ScheduleUpdatePrefsOnNetworkThread(SET_ALTERNATIVE_SERVICES); } return changed; } bool HttpServerPropertiesManager::SetAlternativeServices( const HostPortPair& origin, const AlternativeServiceInfoVector& alternative_service_info_vector) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); const bool changed = http_server_properties_impl_->SetAlternativeServices( origin, alternative_service_info_vector); if (changed) { ScheduleUpdatePrefsOnNetworkThread(SET_ALTERNATIVE_SERVICES); } return changed; } void HttpServerPropertiesManager::MarkAlternativeServiceBroken( const AlternativeService& alternative_service) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); http_server_properties_impl_->MarkAlternativeServiceBroken( alternative_service); ScheduleUpdatePrefsOnNetworkThread(MARK_ALTERNATIVE_SERVICE_BROKEN); } void HttpServerPropertiesManager::MarkAlternativeServiceRecentlyBroken( const AlternativeService& alternative_service) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); http_server_properties_impl_->MarkAlternativeServiceRecentlyBroken( alternative_service); ScheduleUpdatePrefsOnNetworkThread(MARK_ALTERNATIVE_SERVICE_RECENTLY_BROKEN); } bool HttpServerPropertiesManager::IsAlternativeServiceBroken( const AlternativeService& alternative_service) const { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->IsAlternativeServiceBroken( alternative_service); } bool HttpServerPropertiesManager::WasAlternativeServiceRecentlyBroken( const AlternativeService& alternative_service) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->WasAlternativeServiceRecentlyBroken( alternative_service); } void HttpServerPropertiesManager::ConfirmAlternativeService( const AlternativeService& alternative_service) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); bool old_value = http_server_properties_impl_->IsAlternativeServiceBroken( alternative_service); http_server_properties_impl_->ConfirmAlternativeService(alternative_service); bool new_value = http_server_properties_impl_->IsAlternativeServiceBroken( alternative_service); // For persisting, we only care about the value returned by // IsAlternativeServiceBroken. If that value changes, then call persist. if (old_value != new_value) ScheduleUpdatePrefsOnNetworkThread(CONFIRM_ALTERNATIVE_SERVICE); } void HttpServerPropertiesManager::ClearAlternativeServices( const HostPortPair& origin) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); const AlternativeServiceMap& map = http_server_properties_impl_->alternative_service_map(); size_t old_size = map.size(); http_server_properties_impl_->ClearAlternativeServices(origin); size_t new_size = map.size(); // Persist only if we have deleted an entry. if (old_size != new_size) ScheduleUpdatePrefsOnNetworkThread(CLEAR_ALTERNATIVE_SERVICE); } const AlternativeServiceMap& HttpServerPropertiesManager::alternative_service_map() const { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->alternative_service_map(); } scoped_ptr HttpServerPropertiesManager::GetAlternativeServiceInfoAsValue() const { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->GetAlternativeServiceInfoAsValue(); } 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_t value) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); bool persist = http_server_properties_impl_->SetSpdySetting( host_port_pair, id, flags, value); if (persist) ScheduleUpdatePrefsOnNetworkThread(SET_SPDY_SETTING); return persist; } void HttpServerPropertiesManager::ClearSpdySettings( const HostPortPair& host_port_pair) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); http_server_properties_impl_->ClearSpdySettings(host_port_pair); ScheduleUpdatePrefsOnNetworkThread(CLEAR_SPDY_SETTINGS); } void HttpServerPropertiesManager::ClearAllSpdySettings() { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); http_server_properties_impl_->ClearAllSpdySettings(); ScheduleUpdatePrefsOnNetworkThread(CLEAR_ALL_SPDY_SETTINGS); } const SpdySettingsMap& HttpServerPropertiesManager::spdy_settings_map() const { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->spdy_settings_map(); } bool HttpServerPropertiesManager::GetSupportsQuic( IPAddress* last_address) const { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->GetSupportsQuic(last_address); } void HttpServerPropertiesManager::SetSupportsQuic(bool used_quic, const IPAddress& address) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); IPAddress old_last_quic_addr; http_server_properties_impl_->GetSupportsQuic(&old_last_quic_addr); http_server_properties_impl_->SetSupportsQuic(used_quic, address); IPAddress new_last_quic_addr; http_server_properties_impl_->GetSupportsQuic(&new_last_quic_addr); if (old_last_quic_addr != new_last_quic_addr) ScheduleUpdatePrefsOnNetworkThread(SET_SUPPORTS_QUIC); } void HttpServerPropertiesManager::SetServerNetworkStats( const HostPortPair& host_port_pair, ServerNetworkStats stats) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); ServerNetworkStats old_stats; const ServerNetworkStats* old_stats_ptr = http_server_properties_impl_->GetServerNetworkStats(host_port_pair); if (http_server_properties_impl_->GetServerNetworkStats(host_port_pair)) old_stats = *old_stats_ptr; http_server_properties_impl_->SetServerNetworkStats(host_port_pair, stats); ServerNetworkStats new_stats = *(http_server_properties_impl_->GetServerNetworkStats(host_port_pair)); if (old_stats != new_stats) ScheduleUpdatePrefsOnNetworkThread(SET_SERVER_NETWORK_STATS); } const ServerNetworkStats* HttpServerPropertiesManager::GetServerNetworkStats( const HostPortPair& host_port_pair) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->GetServerNetworkStats(host_port_pair); } const ServerNetworkStatsMap& HttpServerPropertiesManager::server_network_stats_map() const { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->server_network_stats_map(); } bool HttpServerPropertiesManager::SetQuicServerInfo( const QuicServerId& server_id, const std::string& server_info) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); bool changed = http_server_properties_impl_->SetQuicServerInfo(server_id, server_info); if (changed) ScheduleUpdatePrefsOnNetworkThread(SET_QUIC_SERVER_INFO); return changed; } const std::string* HttpServerPropertiesManager::GetQuicServerInfo( const QuicServerId& server_id) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->GetQuicServerInfo(server_id); } const QuicServerInfoMap& HttpServerPropertiesManager::quic_server_info_map() const { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->quic_server_info_map(); } size_t HttpServerPropertiesManager::max_server_configs_stored_in_properties() const { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_ ->max_server_configs_stored_in_properties(); } void HttpServerPropertiesManager::SetMaxServerConfigsStoredInProperties( size_t max_server_configs_stored_in_properties) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); return http_server_properties_impl_->SetMaxServerConfigsStoredInProperties( max_server_configs_stored_in_properties); } // // 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_delegate_->HasServerProperties()) return; bool detected_corrupted_prefs = false; const base::DictionaryValue& http_server_properties_dict = pref_delegate_->GetServerProperties(); int version = kMissingVersion; if (!http_server_properties_dict.GetIntegerWithoutPathExpansion(kVersionKey, &version)) { DVLOG(1) << "Missing version. Clearing all properties."; return; } const base::DictionaryValue* servers_dict = nullptr; const base::ListValue* servers_list = nullptr; if (version < 4) { // The properties for a given server is in // http_server_properties_dict["servers"][server]. // Before Version 4, server data was stored in the following format in // alphabetical order. // // "http_server_properties": { // "servers": { // "0-edge-chat.facebook.com:443" : {...}, // "0.client-channel.google.com:443" : {...}, // "yt3.ggpht.com:443" : {...}, // ... // }, ... // }, if (!http_server_properties_dict.GetDictionaryWithoutPathExpansion( kServersKey, &servers_dict)) { DVLOG(1) << "Malformed http_server_properties for servers."; return; } } else { // From Version 4 onwards, data was stored in the following format. // |servers| are saved in MRU order. // // "http_server_properties": { // "servers": [ // {"yt3.ggpht.com:443" : {...}}, // {"0.client-channel.google.com:443" : {...}}, // {"0-edge-chat.facebook.com:443" : {...}}, // ... // ], ... // }, if (!http_server_properties_dict.GetListWithoutPathExpansion( kServersKey, &servers_list)) { DVLOG(1) << "Malformed http_server_properties for servers list."; return; } } IPAddress* addr = new IPAddress; ReadSupportsQuic(http_server_properties_dict, addr); // String is host/port pair of spdy server. scoped_ptr spdy_servers(new ServerList); scoped_ptr spdy_settings_map( new SpdySettingsMap(kMaxSpdySettingsHostsToPersist)); scoped_ptr alternative_service_map( new AlternativeServiceMap(kMaxAlternateProtocolHostsToPersist)); scoped_ptr server_network_stats_map( new ServerNetworkStatsMap(kMaxServerNetworkStatsHostsToPersist)); scoped_ptr quic_server_info_map( new QuicServerInfoMap(QuicServerInfoMap::NO_AUTO_EVICT)); if (version < 4) { if (!AddServersData(*servers_dict, spdy_servers.get(), spdy_settings_map.get(), alternative_service_map.get(), server_network_stats_map.get())) { detected_corrupted_prefs = true; } } else { for (base::ListValue::const_iterator it = servers_list->begin(); it != servers_list->end(); ++it) { if (!(*it)->GetAsDictionary(&servers_dict)) { DVLOG(1) << "Malformed http_server_properties for servers dictionary."; detected_corrupted_prefs = true; continue; } if (!AddServersData( *servers_dict, spdy_servers.get(), spdy_settings_map.get(), alternative_service_map.get(), server_network_stats_map.get())) { detected_corrupted_prefs = true; } } } if (!AddToQuicServerInfoMap(http_server_properties_dict, quic_server_info_map.get())) { detected_corrupted_prefs = true; } 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(alternative_service_map.release()), base::Owned(addr), base::Owned(server_network_stats_map.release()), base::Owned(quic_server_info_map.release()), detected_corrupted_prefs)); } bool HttpServerPropertiesManager::AddServersData( const base::DictionaryValue& servers_dict, ServerList* spdy_servers, SpdySettingsMap* spdy_settings_map, AlternativeServiceMap* alternative_service_map, ServerNetworkStatsMap* network_stats_map) { for (base::DictionaryValue::Iterator it(servers_dict); !it.IsAtEnd(); it.Advance()) { // Get server's host/pair. const std::string& server_str = it.key(); HostPortPair server = HostPortPair::FromString(server_str); if (server.host().empty()) { DVLOG(1) << "Malformed http_server_properties for server: " << server_str; return false; } const base::DictionaryValue* server_pref_dict = nullptr; if (!it.value().GetAsDictionary(&server_pref_dict)) { DVLOG(1) << "Malformed http_server_properties server: " << server_str; return false; } // Get if server supports Spdy. bool supports_spdy = false; if ((server_pref_dict->GetBoolean(kSupportsSpdyKey, &supports_spdy)) && supports_spdy) { spdy_servers->push_back(server_str); } AddToSpdySettingsMap(server, *server_pref_dict, spdy_settings_map); if (!AddToAlternativeServiceMap(server, *server_pref_dict, alternative_service_map) || !AddToNetworkStatsMap(server, *server_pref_dict, network_stats_map)) { return false; } } return true; } void HttpServerPropertiesManager::AddToSpdySettingsMap( const HostPortPair& server, const base::DictionaryValue& server_pref_dict, SpdySettingsMap* spdy_settings_map) { // Get SpdySettings. DCHECK(spdy_settings_map->Peek(server) == spdy_settings_map->end()); const base::DictionaryValue* spdy_settings_dict = nullptr; if (!server_pref_dict.GetDictionaryWithoutPathExpansion( kSettingsKey, &spdy_settings_dict)) { return; } 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.ToString(); NOTREACHED(); continue; } int value = 0; if (!dict_it.value().GetAsInteger(&value)) { DVLOG(1) << "Malformed value in SpdySettings for server: " << server.ToString(); NOTREACHED(); continue; } SettingsFlagsAndValue flags_and_value(SETTINGS_FLAG_PERSISTED, value); settings_map[static_cast(id)] = flags_and_value; } spdy_settings_map->Put(server, settings_map); } bool HttpServerPropertiesManager::ParseAlternativeServiceDict( const base::DictionaryValue& alternative_service_dict, const std::string& server_str, AlternativeServiceInfo* alternative_service_info) { // Protocol is mandatory. std::string protocol_str; if (!alternative_service_dict.GetStringWithoutPathExpansion(kProtocolKey, &protocol_str)) { DVLOG(1) << "Malformed alternative service protocol string for server: " << server_str; return false; } AlternateProtocol protocol = AlternateProtocolFromString(protocol_str); if (!IsAlternateProtocolValid(protocol)) { DVLOG(1) << "Invalid alternative service protocol string for server: " << server_str; return false; } alternative_service_info->alternative_service.protocol = protocol; // Host is optional, defaults to "". alternative_service_info->alternative_service.host.clear(); if (alternative_service_dict.HasKey(kHostKey) && !alternative_service_dict.GetStringWithoutPathExpansion( kHostKey, &(alternative_service_info->alternative_service.host))) { DVLOG(1) << "Malformed alternative service host string for server: " << server_str; return false; } // Port is mandatory. int port = 0; if (!alternative_service_dict.GetInteger(kPortKey, &port) || !IsPortValid(port)) { DVLOG(1) << "Malformed alternative service port for server: " << server_str; return false; } alternative_service_info->alternative_service.port = static_cast(port); // Expiration is optional, defaults to one day. base::Time expiration; if (!alternative_service_dict.HasKey(kExpirationKey)) { alternative_service_info->expiration = base::Time::Now() + base::TimeDelta::FromDays(1); return true; } std::string expiration_string; if (alternative_service_dict.GetStringWithoutPathExpansion( kExpirationKey, &expiration_string)) { int64_t expiration_int64 = 0; if (!base::StringToInt64(expiration_string, &expiration_int64)) { DVLOG(1) << "Malformed alternative service expiration for server: " << server_str; return false; } alternative_service_info->expiration = base::Time::FromInternalValue(expiration_int64); return true; } DVLOG(1) << "Malformed alternative service expiration for server: " << server_str; return false; } bool HttpServerPropertiesManager::AddToAlternativeServiceMap( const HostPortPair& server, const base::DictionaryValue& server_pref_dict, AlternativeServiceMap* alternative_service_map) { DCHECK(alternative_service_map->Peek(server) == alternative_service_map->end()); const base::ListValue* alternative_service_list; if (!server_pref_dict.GetListWithoutPathExpansion( kAlternativeServiceKey, &alternative_service_list)) { return true; } AlternativeServiceInfoVector alternative_service_info_vector; for (const base::Value* alternative_service_list_item : *alternative_service_list) { const base::DictionaryValue* alternative_service_dict; if (!alternative_service_list_item->GetAsDictionary( &alternative_service_dict)) return false; AlternativeServiceInfo alternative_service_info; if (!ParseAlternativeServiceDict(*alternative_service_dict, server.ToString(), &alternative_service_info)) { return false; } if (base::Time::Now() < alternative_service_info.expiration) { alternative_service_info_vector.push_back(alternative_service_info); } } if (alternative_service_info_vector.empty()) { return false; } alternative_service_map->Put(server, alternative_service_info_vector); return true; } bool HttpServerPropertiesManager::ReadSupportsQuic( const base::DictionaryValue& http_server_properties_dict, IPAddress* last_quic_address) { const base::DictionaryValue* supports_quic_dict = nullptr; if (!http_server_properties_dict.GetDictionaryWithoutPathExpansion( kSupportsQuicKey, &supports_quic_dict)) { return true; } bool used_quic = false; if (!supports_quic_dict->GetBooleanWithoutPathExpansion(kUsedQuicKey, &used_quic)) { DVLOG(1) << "Malformed SupportsQuic"; return false; } if (!used_quic) return false; std::string address; if (!supports_quic_dict->GetStringWithoutPathExpansion(kAddressKey, &address) || !last_quic_address->AssignFromIPLiteral(address)) { DVLOG(1) << "Malformed SupportsQuic"; return false; } return true; } bool HttpServerPropertiesManager::AddToNetworkStatsMap( const HostPortPair& server, const base::DictionaryValue& server_pref_dict, ServerNetworkStatsMap* network_stats_map) { DCHECK(network_stats_map->Peek(server) == network_stats_map->end()); const base::DictionaryValue* server_network_stats_dict = nullptr; if (!server_pref_dict.GetDictionaryWithoutPathExpansion( kNetworkStatsKey, &server_network_stats_dict)) { return true; } int srtt; if (!server_network_stats_dict->GetIntegerWithoutPathExpansion(kSrttKey, &srtt)) { DVLOG(1) << "Malformed ServerNetworkStats for server: " << server.ToString(); return false; } ServerNetworkStats server_network_stats; server_network_stats.srtt = base::TimeDelta::FromInternalValue(srtt); // TODO(rtenneti): When QUIC starts using bandwidth_estimate, then persist // bandwidth_estimate. network_stats_map->Put(server, server_network_stats); return true; } bool HttpServerPropertiesManager::AddToQuicServerInfoMap( const base::DictionaryValue& http_server_properties_dict, QuicServerInfoMap* quic_server_info_map) { const base::DictionaryValue* quic_servers_dict = nullptr; if (!http_server_properties_dict.GetDictionaryWithoutPathExpansion( kQuicServers, &quic_servers_dict)) { DVLOG(1) << "Malformed http_server_properties for quic_servers."; return true; } bool detected_corrupted_prefs = false; for (base::DictionaryValue::Iterator it(*quic_servers_dict); !it.IsAtEnd(); it.Advance()) { // Get quic_server_id. const std::string& quic_server_id_str = it.key(); QuicServerId quic_server_id = QuicServerId::FromString(quic_server_id_str); if (quic_server_id.host().empty()) { DVLOG(1) << "Malformed http_server_properties for quic server: " << quic_server_id_str; detected_corrupted_prefs = true; continue; } const base::DictionaryValue* quic_server_pref_dict = nullptr; if (!it.value().GetAsDictionary(&quic_server_pref_dict)) { DVLOG(1) << "Malformed http_server_properties quic server dict: " << quic_server_id_str; detected_corrupted_prefs = true; continue; } std::string quic_server_info; if (!quic_server_pref_dict->GetStringWithoutPathExpansion( kServerInfoKey, &quic_server_info)) { DVLOG(1) << "Malformed http_server_properties quic server info: " << quic_server_id_str; detected_corrupted_prefs = true; continue; } quic_server_info_map->Put(quic_server_id, quic_server_info); } return !detected_corrupted_prefs; } void HttpServerPropertiesManager::UpdateCacheFromPrefsOnNetworkThread( ServerList* spdy_servers, SpdySettingsMap* spdy_settings_map, AlternativeServiceMap* alternative_service_map, IPAddress* last_quic_address, ServerNetworkStatsMap* server_network_stats_map, QuicServerInfoMap* quic_server_info_map, 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 alternative service list from // preferences. UMA_HISTOGRAM_COUNTS("Net.CountOfAlternateProtocolServers", alternative_service_map->size()); http_server_properties_impl_->InitializeAlternativeServiceServers( alternative_service_map); http_server_properties_impl_->InitializeSupportsQuic(last_quic_address); http_server_properties_impl_->InitializeServerNetworkStats( server_network_stats_map); http_server_properties_impl_->InitializeQuicServerInfoMap( quic_server_info_map); // Update the prefs with what we have read (delete all corrupted prefs). if (detected_corrupted_prefs) ScheduleUpdatePrefsOnNetworkThread(DETECTED_CORRUPTED_PREFS); } // // Update Preferences with data from the cached data. // void HttpServerPropertiesManager::ScheduleUpdatePrefsOnNetworkThread( Location location) { DCHECK(network_task_runner_->RunsTasksOnCurrentThread()); // Cancel pending updates, if any. network_prefs_update_timer_->Stop(); StartPrefsUpdateTimerOnNetworkThread( base::TimeDelta::FromMilliseconds(kUpdatePrefsDelayMs)); // TODO(rtenneti): Delete the following histogram after collecting some data. UMA_HISTOGRAM_ENUMERATION("Net.HttpServerProperties.UpdatePrefs", location, HttpServerPropertiesManager::NUM_LOCATIONS); } 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()); // It is in MRU order. base::ListValue* spdy_server_list = new base::ListValue; http_server_properties_impl_->GetSpdyServerList( spdy_server_list, kMaxSupportsSpdyServerHostsToPersist); SpdySettingsMap* spdy_settings_map = new SpdySettingsMap(kMaxSpdySettingsHostsToPersist); const SpdySettingsMap& main_map = http_server_properties_impl_->spdy_settings_map(); int count = 0; // Maintain MRU order. for (SpdySettingsMap::const_reverse_iterator it = main_map.rbegin(); it != main_map.rend() && count < kMaxSpdySettingsHostsToPersist; ++it, ++count) { spdy_settings_map->Put(it->first, it->second); } AlternativeServiceMap* alternative_service_map = new AlternativeServiceMap(kMaxAlternateProtocolHostsToPersist); const AlternativeServiceMap& map = http_server_properties_impl_->alternative_service_map(); UMA_HISTOGRAM_COUNTS("Net.CountOfAlternateProtocolServers.Memory", map.size()); count = 0; typedef std::map CanonicalHostPersistedMap; CanonicalHostPersistedMap persisted_map; // Maintain MRU order. for (AlternativeServiceMap::const_reverse_iterator it = map.rbegin(); it != map.rend() && count < kMaxAlternateProtocolHostsToPersist; ++it) { const HostPortPair& server = it->first; AlternativeServiceInfoVector notbroken_alternative_service_info_vector; for (const AlternativeServiceInfo& alternative_service_info : it->second) { // Do not persist expired entries. if (alternative_service_info.expiration < base::Time::Now()) { continue; } AlternativeService alternative_service( alternative_service_info.alternative_service); if (!IsAlternateProtocolValid(alternative_service.protocol)) { continue; } if (alternative_service.host.empty()) { alternative_service.host = server.host(); } if (IsAlternativeServiceBroken(alternative_service)) { continue; } notbroken_alternative_service_info_vector.push_back( alternative_service_info); } if (notbroken_alternative_service_info_vector.empty()) { continue; } std::string canonical_suffix = http_server_properties_impl_->GetCanonicalSuffix(server.host()); if (!canonical_suffix.empty()) { if (persisted_map.find(canonical_suffix) != persisted_map.end()) continue; persisted_map[canonical_suffix] = true; } alternative_service_map->Put(server, notbroken_alternative_service_info_vector); ++count; } ServerNetworkStatsMap* server_network_stats_map = new ServerNetworkStatsMap(kMaxServerNetworkStatsHostsToPersist); const ServerNetworkStatsMap& network_stats_map = http_server_properties_impl_->server_network_stats_map(); count = 0; for (ServerNetworkStatsMap::const_reverse_iterator it = network_stats_map.rbegin(); it != network_stats_map.rend() && count < kMaxServerNetworkStatsHostsToPersist; ++it, ++count) { server_network_stats_map->Put(it->first, it->second); } QuicServerInfoMap* quic_server_info_map = nullptr; const QuicServerInfoMap& main_quic_server_info_map = http_server_properties_impl_->quic_server_info_map(); if (main_quic_server_info_map.size() > 0) { quic_server_info_map = new QuicServerInfoMap(max_server_configs_stored_in_properties()); for (const std::pair& entry : main_quic_server_info_map) { quic_server_info_map->Put(entry.first, entry.second); } } IPAddress* last_quic_addr = new IPAddress; http_server_properties_impl_->GetSupportsQuic(last_quic_addr); // 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(alternative_service_map), base::Owned(last_quic_addr), base::Owned(server_network_stats_map), base::Owned(quic_server_info_map), completion)); } // A local or temporary data structure to hold |supports_spdy|, SpdySettings, // AlternativeServiceInfoVector, and SupportsQuic preferences for a server. This // is used only in UpdatePrefsOnPrefThread. struct ServerPref { ServerPref() : supports_spdy(false), settings_map(nullptr), alternative_service_info_vector(nullptr), supports_quic(nullptr), server_network_stats(nullptr) {} ServerPref( bool supports_spdy, const SettingsMap* settings_map, const AlternativeServiceInfoVector* alternative_service_info_vector, const SupportsQuic* supports_quic, const ServerNetworkStats* server_network_stats) : supports_spdy(supports_spdy), settings_map(settings_map), alternative_service_info_vector(alternative_service_info_vector), supports_quic(supports_quic), server_network_stats(server_network_stats) {} bool supports_spdy; const SettingsMap* settings_map; const AlternativeServiceInfoVector* alternative_service_info_vector; const SupportsQuic* supports_quic; const ServerNetworkStats* server_network_stats; }; // All maps and lists are in MRU order. void HttpServerPropertiesManager::UpdatePrefsOnPrefThread( base::ListValue* spdy_server_list, SpdySettingsMap* spdy_settings_map, AlternativeServiceMap* alternative_service_map, IPAddress* last_quic_address, ServerNetworkStatsMap* server_network_stats_map, QuicServerInfoMap* quic_server_info_map, const base::Closure& completion) { typedef base::MRUCache ServerPrefMap; ServerPrefMap server_pref_map(ServerPrefMap::NO_AUTO_EVICT); DCHECK(pref_task_runner_->RunsTasksOnCurrentThread()); // Add servers that support spdy to server_pref_map in the MRU order. for (size_t index = spdy_server_list->GetSize(); index > 0; --index) { std::string s; if (spdy_server_list->GetString(index - 1, &s)) { HostPortPair server = HostPortPair::FromString(s); ServerPrefMap::iterator it = server_pref_map.Get(server); if (it == server_pref_map.end()) { ServerPref server_pref; server_pref.supports_spdy = true; server_pref_map.Put(server, server_pref); } else { it->second.supports_spdy = true; } } } // Add servers that have SpdySettings to server_pref_map in the MRU order. for (SpdySettingsMap::reverse_iterator map_it = spdy_settings_map->rbegin(); map_it != spdy_settings_map->rend(); ++map_it) { const HostPortPair& server = map_it->first; ServerPrefMap::iterator it = server_pref_map.Get(server); if (it == server_pref_map.end()) { ServerPref server_pref; server_pref.settings_map = &map_it->second; server_pref_map.Put(server, server_pref); } else { it->second.settings_map = &map_it->second; } } // Add alternative services to server_pref_map in the MRU order. for (AlternativeServiceMap::const_reverse_iterator map_it = alternative_service_map->rbegin(); map_it != alternative_service_map->rend(); ++map_it) { const HostPortPair& server = map_it->first; ServerPrefMap::iterator it = server_pref_map.Get(server); if (it == server_pref_map.end()) { ServerPref server_pref; server_pref.alternative_service_info_vector = &map_it->second; server_pref_map.Put(server, server_pref); } else { it->second.alternative_service_info_vector = &map_it->second; } } // Add ServerNetworkStats servers to server_pref_map in the MRU order. for (ServerNetworkStatsMap::const_reverse_iterator map_it = server_network_stats_map->rbegin(); map_it != server_network_stats_map->rend(); ++map_it) { const HostPortPair& server = map_it->first; ServerPrefMap::iterator it = server_pref_map.Get(server); if (it == server_pref_map.end()) { ServerPref server_pref; server_pref.server_network_stats = &map_it->second; server_pref_map.Put(server, server_pref); } else { it->second.server_network_stats = &map_it->second; } } // Persist properties to the prefs in the MRU order. base::DictionaryValue http_server_properties_dict; base::ListValue* servers_list = new base::ListValue; for (ServerPrefMap::const_reverse_iterator map_it = server_pref_map.rbegin(); map_it != server_pref_map.rend(); ++map_it) { const HostPortPair& server = map_it->first; const ServerPref& server_pref = map_it->second; base::DictionaryValue* servers_dict = new base::DictionaryValue; base::DictionaryValue* server_pref_dict = new base::DictionaryValue; // Save supports_spdy. if (server_pref.supports_spdy) server_pref_dict->SetBoolean(kSupportsSpdyKey, server_pref.supports_spdy); SaveSpdySettingsToServerPrefs(server_pref.settings_map, server_pref_dict); SaveAlternativeServiceToServerPrefs( server_pref.alternative_service_info_vector, server_pref_dict); SaveNetworkStatsToServerPrefs(server_pref.server_network_stats, server_pref_dict); servers_dict->SetWithoutPathExpansion(server.ToString(), server_pref_dict); bool value = servers_list->AppendIfNotPresent(servers_dict); DCHECK(value); // Should never happen. } http_server_properties_dict.SetWithoutPathExpansion(kServersKey, servers_list); SetVersion(&http_server_properties_dict, kVersionNumber); SaveSupportsQuicToPrefs(last_quic_address, &http_server_properties_dict); SaveQuicServerInfoMapToServerPrefs(quic_server_info_map, &http_server_properties_dict); setting_prefs_ = true; pref_delegate_->SetServerProperties(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::SaveSpdySettingsToServerPrefs( const SettingsMap* settings_map, base::DictionaryValue* server_pref_dict) { if (!settings_map) { return; } base::DictionaryValue* spdy_settings_dict = new base::DictionaryValue; for (SettingsMap::const_iterator it = settings_map->begin(); it != settings_map->end(); ++it) { SpdySettingsIds id = it->first; uint32_t value = it->second.second; std::string key = base::StringPrintf("%u", id); spdy_settings_dict->SetInteger(key, value); } server_pref_dict->SetWithoutPathExpansion(kSettingsKey, spdy_settings_dict); } void HttpServerPropertiesManager::SaveAlternativeServiceToServerPrefs( const AlternativeServiceInfoVector* alternative_service_info_vector, base::DictionaryValue* server_pref_dict) { if (!alternative_service_info_vector || alternative_service_info_vector->empty()) { return; } scoped_ptr alternative_service_list(new base::ListValue); for (const AlternativeServiceInfo& alternative_service_info : *alternative_service_info_vector) { const AlternativeService alternative_service = alternative_service_info.alternative_service; DCHECK(IsAlternateProtocolValid(alternative_service.protocol)); base::DictionaryValue* alternative_service_dict = new base::DictionaryValue; alternative_service_dict->SetInteger(kPortKey, alternative_service.port); if (!alternative_service.host.empty()) { alternative_service_dict->SetString(kHostKey, alternative_service.host); } alternative_service_dict->SetString( kProtocolKey, AlternateProtocolToString(alternative_service.protocol)); // JSON cannot store int64_t, so expiration is converted to a string. alternative_service_dict->SetString( kExpirationKey, base::Int64ToString( alternative_service_info.expiration.ToInternalValue())); alternative_service_list->Append(alternative_service_dict); } if (alternative_service_list->GetSize() == 0) return; server_pref_dict->SetWithoutPathExpansion(kAlternativeServiceKey, alternative_service_list.release()); } void HttpServerPropertiesManager::SaveSupportsQuicToPrefs( const IPAddress* last_quic_address, base::DictionaryValue* http_server_properties_dict) { if (!last_quic_address || !last_quic_address->IsValid()) return; base::DictionaryValue* supports_quic_dict = new base::DictionaryValue; supports_quic_dict->SetBoolean(kUsedQuicKey, true); supports_quic_dict->SetString(kAddressKey, last_quic_address->ToString()); http_server_properties_dict->SetWithoutPathExpansion(kSupportsQuicKey, supports_quic_dict); } void HttpServerPropertiesManager::SaveNetworkStatsToServerPrefs( const ServerNetworkStats* server_network_stats, base::DictionaryValue* server_pref_dict) { if (!server_network_stats) return; base::DictionaryValue* server_network_stats_dict = new base::DictionaryValue; // Becasue JSON doesn't support int64_t, persist int64_t as a string. server_network_stats_dict->SetInteger( kSrttKey, static_cast(server_network_stats->srtt.ToInternalValue())); // TODO(rtenneti): When QUIC starts using bandwidth_estimate, then persist // bandwidth_estimate. server_pref_dict->SetWithoutPathExpansion(kNetworkStatsKey, server_network_stats_dict); } void HttpServerPropertiesManager::SaveQuicServerInfoMapToServerPrefs( QuicServerInfoMap* quic_server_info_map, base::DictionaryValue* http_server_properties_dict) { if (!quic_server_info_map) return; base::DictionaryValue* quic_servers_dict = new base::DictionaryValue; for (const std::pair& entry : *quic_server_info_map) { const QuicServerId& server_id = entry.first; base::DictionaryValue* quic_server_pref_dict = new base::DictionaryValue; quic_server_pref_dict->SetStringWithoutPathExpansion(kServerInfoKey, entry.second); quic_servers_dict->SetWithoutPathExpansion(server_id.ToString(), quic_server_pref_dict); } http_server_properties_dict->SetWithoutPathExpansion(kQuicServers, quic_servers_dict); } void HttpServerPropertiesManager::OnHttpServerPropertiesChanged() { DCHECK(pref_task_runner_->RunsTasksOnCurrentThread()); if (!setting_prefs_) ScheduleUpdateCacheOnPrefThread(); } } // namespace net