| // 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 <utility> |
| |
| #include "base/bind.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/values.h" |
| #include "net/base/host_port_pair.h" |
| #include "net/base/ip_address.h" |
| #include "net/base/port_util.h" |
| #include "net/base/privacy_mode.h" |
| #include "net/third_party/quic/platform/api/quic_hostname_utils.h" |
| #include "url/gurl.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 be a |
| // no-op. |
| constexpr base::TimeDelta kUpdateCacheDelay = base::TimeDelta::FromSeconds(1); |
| |
| // Time to wait before starting an update the preferences from the |
| // http_server_properties_impl_ cache. Scheduling another update during this |
| // period will be a no-op. |
| constexpr base::TimeDelta kUpdatePrefsDelay = base::TimeDelta::FromSeconds(60); |
| |
| // "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 = 5; |
| |
| // Persist at most 200 currently-broken alternative services to disk. |
| const int kMaxBrokenAlternativeServicesToPersist = 200; |
| |
| const char kVersionKey[] = "version"; |
| const char kServersKey[] = "servers"; |
| const char kSupportsSpdyKey[] = "supports_spdy"; |
| 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 kAdvertisedVersionsKey[] = "advertised_versions"; |
| const char kNetworkStatsKey[] = "network_stats"; |
| const char kSrttKey[] = "srtt"; |
| const char kBrokenAlternativeServicesKey[] = "broken_alternative_services"; |
| const char kBrokenUntilKey[] = "broken_until"; |
| const char kBrokenCountKey[] = "broken_count"; |
| |
| void AddAlternativeServiceFieldsToDictionaryValue( |
| const AlternativeService& alternative_service, |
| base::DictionaryValue* dict) { |
| dict->SetInteger(kPortKey, alternative_service.port); |
| if (!alternative_service.host.empty()) { |
| dict->SetString(kHostKey, alternative_service.host); |
| } |
| dict->SetString(kProtocolKey, |
| NextProtoToString(alternative_service.protocol)); |
| } |
| |
| std::unique_ptr<base::Value> NetLogCallback( |
| const base::Value* http_server_properties_dict, |
| NetLogCaptureMode capture_mode) { |
| return http_server_properties_dict->CreateDeepCopy(); |
| } |
| |
| // A local or temporary data structure to hold preferences for a server. |
| // This is used only in UpdatePrefs. |
| struct ServerPref { |
| bool supports_spdy = false; |
| AlternativeServiceInfoVector alternative_service_info_vector; |
| bool server_network_stats_valid = false; |
| ServerNetworkStats server_network_stats; |
| }; |
| |
| quic::QuicServerId QuicServerIdFromString(const std::string& str) { |
| GURL url(str); |
| if (!url.is_valid()) { |
| return quic::QuicServerId(); |
| } |
| HostPortPair host_port_pair = HostPortPair::FromURL(url); |
| return quic::QuicServerId(host_port_pair.host(), host_port_pair.port(), |
| url.path_piece() == "/private" |
| ? PRIVACY_MODE_ENABLED |
| : PRIVACY_MODE_DISABLED); |
| } |
| |
| std::string QuicServerIdToString(const quic::QuicServerId& server_id) { |
| HostPortPair host_port_pair(server_id.host(), server_id.port()); |
| return "https://" + host_port_pair.ToString() + |
| (server_id.privacy_mode_enabled() ? "/private" : ""); |
| } |
| |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // HttpServerPropertiesManager |
| |
| HttpServerPropertiesManager::PrefDelegate::~PrefDelegate() = default; |
| |
| HttpServerPropertiesManager::HttpServerPropertiesManager( |
| std::unique_ptr<PrefDelegate> pref_delegate, |
| NetLog* net_log, |
| const base::TickClock* clock) |
| : pref_delegate_(std::move(pref_delegate)), |
| clock_(clock ? clock : base::DefaultTickClock::GetInstance()), |
| net_log_( |
| NetLogWithSource::Make(net_log, |
| NetLogSourceType::HTTP_SERVER_PROPERTIES)) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(pref_delegate_); |
| DCHECK(clock_); |
| |
| pref_delegate_->StartListeningForUpdates(base::BindRepeating( |
| &HttpServerPropertiesManager::OnHttpServerPropertiesChanged, |
| base::Unretained(this))); |
| net_log_.BeginEvent(NetLogEventType::HTTP_SERVER_PROPERTIES_INITIALIZATION); |
| |
| http_server_properties_impl_.reset( |
| new HttpServerPropertiesImpl(clock_, nullptr)); |
| } |
| |
| HttpServerPropertiesManager::~HttpServerPropertiesManager() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| // Flush settings on destruction. |
| UpdatePrefsFromCache(base::OnceClosure()); |
| } |
| |
| // 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); |
| } |
| |
| void HttpServerPropertiesManager::Clear(base::OnceClosure callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| http_server_properties_impl_->Clear(base::OnceClosure()); |
| UpdatePrefsFromCache(std::move(callback)); |
| } |
| |
| bool HttpServerPropertiesManager::SupportsRequestPriority( |
| const url::SchemeHostPort& server) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->SupportsRequestPriority(server); |
| } |
| |
| bool HttpServerPropertiesManager::GetSupportsSpdy( |
| const url::SchemeHostPort& server) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->GetSupportsSpdy(server); |
| } |
| |
| void HttpServerPropertiesManager::SetSupportsSpdy( |
| const url::SchemeHostPort& server, |
| bool support_spdy) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| 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) |
| ScheduleUpdatePrefs(SUPPORTS_SPDY); |
| } |
| |
| bool HttpServerPropertiesManager::RequiresHTTP11(const HostPortPair& server) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->RequiresHTTP11(server); |
| } |
| |
| void HttpServerPropertiesManager::SetHTTP11Required( |
| const HostPortPair& server) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| http_server_properties_impl_->SetHTTP11Required(server); |
| ScheduleUpdatePrefs(HTTP_11_REQUIRED); |
| } |
| |
| void HttpServerPropertiesManager::MaybeForceHTTP11(const HostPortPair& server, |
| SSLConfig* ssl_config) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| http_server_properties_impl_->MaybeForceHTTP11(server, ssl_config); |
| } |
| |
| AlternativeServiceInfoVector |
| HttpServerPropertiesManager::GetAlternativeServiceInfos( |
| const url::SchemeHostPort& origin) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->GetAlternativeServiceInfos(origin); |
| } |
| |
| bool HttpServerPropertiesManager::SetHttp2AlternativeService( |
| const url::SchemeHostPort& origin, |
| const AlternativeService& alternative_service, |
| base::Time expiration) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| const bool changed = http_server_properties_impl_->SetHttp2AlternativeService( |
| origin, alternative_service, expiration); |
| if (changed) { |
| ScheduleUpdatePrefs(SET_ALTERNATIVE_SERVICES); |
| } |
| return changed; |
| } |
| |
| bool HttpServerPropertiesManager::SetQuicAlternativeService( |
| const url::SchemeHostPort& origin, |
| const AlternativeService& alternative_service, |
| base::Time expiration, |
| const quic::QuicTransportVersionVector& advertised_versions) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| const bool changed = http_server_properties_impl_->SetQuicAlternativeService( |
| origin, alternative_service, expiration, advertised_versions); |
| if (changed) { |
| ScheduleUpdatePrefs(SET_ALTERNATIVE_SERVICES); |
| } |
| return changed; |
| } |
| |
| bool HttpServerPropertiesManager::SetAlternativeServices( |
| const url::SchemeHostPort& origin, |
| const AlternativeServiceInfoVector& alternative_service_info_vector) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| const bool changed = http_server_properties_impl_->SetAlternativeServices( |
| origin, alternative_service_info_vector); |
| if (changed) { |
| ScheduleUpdatePrefs(SET_ALTERNATIVE_SERVICES); |
| } |
| return changed; |
| } |
| |
| void HttpServerPropertiesManager::MarkAlternativeServiceBroken( |
| const AlternativeService& alternative_service) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| http_server_properties_impl_->MarkAlternativeServiceBroken( |
| alternative_service); |
| ScheduleUpdatePrefs(MARK_ALTERNATIVE_SERVICE_BROKEN); |
| } |
| |
| void HttpServerPropertiesManager:: |
| MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( |
| const AlternativeService& alternative_service) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| http_server_properties_impl_ |
| ->MarkAlternativeServiceBrokenUntilDefaultNetworkChanges( |
| alternative_service); |
| ScheduleUpdatePrefs( |
| MARK_ALTERNATIVE_SERVICE_BROKEN_UNTIL_DEFAULT_NETWORK_CHANGES); |
| } |
| |
| void HttpServerPropertiesManager::MarkAlternativeServiceRecentlyBroken( |
| const AlternativeService& alternative_service) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| http_server_properties_impl_->MarkAlternativeServiceRecentlyBroken( |
| alternative_service); |
| ScheduleUpdatePrefs(MARK_ALTERNATIVE_SERVICE_RECENTLY_BROKEN); |
| } |
| |
| bool HttpServerPropertiesManager::IsAlternativeServiceBroken( |
| const AlternativeService& alternative_service) const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->IsAlternativeServiceBroken( |
| alternative_service); |
| } |
| |
| bool HttpServerPropertiesManager::WasAlternativeServiceRecentlyBroken( |
| const AlternativeService& alternative_service) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->WasAlternativeServiceRecentlyBroken( |
| alternative_service); |
| } |
| |
| void HttpServerPropertiesManager::ConfirmAlternativeService( |
| const AlternativeService& alternative_service) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 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) |
| ScheduleUpdatePrefs(CONFIRM_ALTERNATIVE_SERVICE); |
| } |
| |
| bool HttpServerPropertiesManager::OnDefaultNetworkChanged() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| bool changed = http_server_properties_impl_->OnDefaultNetworkChanged(); |
| if (changed) |
| ScheduleUpdatePrefs(ON_DEFAULT_NETWORK_CHANGED); |
| return changed; |
| } |
| |
| const AlternativeServiceMap& |
| HttpServerPropertiesManager::alternative_service_map() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->alternative_service_map(); |
| } |
| |
| std::unique_ptr<base::Value> |
| HttpServerPropertiesManager::GetAlternativeServiceInfoAsValue() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->GetAlternativeServiceInfoAsValue(); |
| } |
| |
| bool HttpServerPropertiesManager::GetSupportsQuic( |
| IPAddress* last_address) const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->GetSupportsQuic(last_address); |
| } |
| |
| void HttpServerPropertiesManager::SetSupportsQuic(bool used_quic, |
| const IPAddress& address) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 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) |
| ScheduleUpdatePrefs(SET_SUPPORTS_QUIC); |
| } |
| |
| void HttpServerPropertiesManager::SetServerNetworkStats( |
| const url::SchemeHostPort& server, |
| ServerNetworkStats stats) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| ServerNetworkStats old_stats; |
| const ServerNetworkStats* old_stats_ptr = |
| http_server_properties_impl_->GetServerNetworkStats(server); |
| if (http_server_properties_impl_->GetServerNetworkStats(server)) |
| old_stats = *old_stats_ptr; |
| http_server_properties_impl_->SetServerNetworkStats(server, stats); |
| ServerNetworkStats new_stats = |
| *(http_server_properties_impl_->GetServerNetworkStats(server)); |
| if (old_stats != new_stats) |
| ScheduleUpdatePrefs(SET_SERVER_NETWORK_STATS); |
| } |
| |
| void HttpServerPropertiesManager::ClearServerNetworkStats( |
| const url::SchemeHostPort& server) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| bool need_update = |
| http_server_properties_impl_->GetServerNetworkStats(server) != nullptr; |
| http_server_properties_impl_->ClearServerNetworkStats(server); |
| if (need_update) |
| ScheduleUpdatePrefs(CLEAR_SERVER_NETWORK_STATS); |
| } |
| |
| const ServerNetworkStats* HttpServerPropertiesManager::GetServerNetworkStats( |
| const url::SchemeHostPort& server) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->GetServerNetworkStats(server); |
| } |
| |
| const ServerNetworkStatsMap& |
| HttpServerPropertiesManager::server_network_stats_map() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->server_network_stats_map(); |
| } |
| |
| bool HttpServerPropertiesManager::SetQuicServerInfo( |
| const quic::QuicServerId& server_id, |
| const std::string& server_info) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| bool changed = |
| http_server_properties_impl_->SetQuicServerInfo(server_id, server_info); |
| if (changed) |
| ScheduleUpdatePrefs(SET_QUIC_SERVER_INFO); |
| return changed; |
| } |
| |
| const std::string* HttpServerPropertiesManager::GetQuicServerInfo( |
| const quic::QuicServerId& server_id) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->GetQuicServerInfo(server_id); |
| } |
| |
| const QuicServerInfoMap& HttpServerPropertiesManager::quic_server_info_map() |
| const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->quic_server_info_map(); |
| } |
| |
| size_t HttpServerPropertiesManager::max_server_configs_stored_in_properties() |
| const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_ |
| ->max_server_configs_stored_in_properties(); |
| } |
| |
| void HttpServerPropertiesManager::SetMaxServerConfigsStoredInProperties( |
| size_t max_server_configs_stored_in_properties) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| return http_server_properties_impl_->SetMaxServerConfigsStoredInProperties( |
| max_server_configs_stored_in_properties); |
| } |
| |
| bool HttpServerPropertiesManager::IsInitialized() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| return is_initialized_; |
| } |
| |
| // static |
| base::TimeDelta HttpServerPropertiesManager::GetUpdateCacheDelayForTesting() { |
| return kUpdateCacheDelay; |
| } |
| |
| // static |
| base::TimeDelta HttpServerPropertiesManager::GetUpdatePrefsDelayForTesting() { |
| return kUpdatePrefsDelay; |
| } |
| |
| void HttpServerPropertiesManager::ScheduleUpdateCacheForTesting() { |
| ScheduleUpdateCache(); |
| } |
| |
| void HttpServerPropertiesManager::ScheduleUpdateCache() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| // Do not schedule a new update if there is already one scheduled. |
| if (pref_cache_update_timer_.IsRunning()) |
| return; |
| |
| if (!is_initialized_) { |
| UpdateCacheFromPrefs(); |
| return; |
| } |
| |
| pref_cache_update_timer_.Start( |
| FROM_HERE, kUpdateCacheDelay, this, |
| &HttpServerPropertiesManager::UpdateCacheFromPrefs); |
| } |
| |
| void HttpServerPropertiesManager::UpdateCacheFromPrefs() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (!is_initialized_) { |
| net_log_.EndEvent(NetLogEventType::HTTP_SERVER_PROPERTIES_INITIALIZATION); |
| is_initialized_ = true; |
| } |
| |
| const base::DictionaryValue* http_server_properties_dict = |
| pref_delegate_->GetServerProperties(); |
| // If there are no preferences set, do nothing. |
| if (!http_server_properties_dict) |
| return; |
| |
| bool detected_corrupted_prefs = false; |
| net_log_.AddEvent(NetLogEventType::HTTP_SERVER_PROPERTIES_UPDATE_CACHE, |
| base::Bind(&NetLogCallback, http_server_properties_dict)); |
| 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:80" : {...}, |
| // ... |
| // }, ... |
| // }, |
| if (!http_server_properties_dict->GetDictionaryWithoutPathExpansion( |
| kServersKey, &servers_dict)) { |
| DVLOG(1) << "Malformed http_server_properties for servers."; |
| return; |
| } |
| } else { |
| // For Version 4, 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:80" : {...}}, |
| // ... |
| // ], ... |
| // }, |
| // For Version 5, data was stored in the following format. |
| // |servers| are saved in MRU order. |servers| are in the format flattened |
| // representation of (scheme/host/port) where port might be ignored if is |
| // default with scheme. |
| // |
| // "http_server_properties": { |
| // "servers": [ |
| // {"https://yt3.ggpht.com" : {...}}, |
| // {"http://0.client-channel.google.com:443" : {...}}, |
| // {"http://0-edge-chat.facebook.com" : {...}}, |
| // ... |
| // ], ... |
| // }, |
| if (!http_server_properties_dict->GetListWithoutPathExpansion( |
| kServersKey, &servers_list)) { |
| DVLOG(1) << "Malformed http_server_properties for servers list."; |
| return; |
| } |
| } |
| |
| std::unique_ptr<IPAddress> addr = std::make_unique<IPAddress>(); |
| ReadSupportsQuic(*http_server_properties_dict, addr.get()); |
| |
| // String is "scheme://host:port" tuple of spdy server. |
| std::unique_ptr<SpdyServersMap> spdy_servers_map = |
| std::make_unique<SpdyServersMap>(); |
| std::unique_ptr<AlternativeServiceMap> alternative_service_map = |
| std::make_unique<AlternativeServiceMap>(); |
| std::unique_ptr<ServerNetworkStatsMap> server_network_stats_map = |
| std::make_unique<ServerNetworkStatsMap>(); |
| std::unique_ptr<QuicServerInfoMap> quic_server_info_map = |
| std::make_unique<QuicServerInfoMap>( |
| max_server_configs_stored_in_properties()); |
| |
| if (version < 4) { |
| if (!AddServersData(*servers_dict, spdy_servers_map.get(), |
| alternative_service_map.get(), |
| server_network_stats_map.get(), version)) { |
| detected_corrupted_prefs = true; |
| } |
| } else { |
| // Iterate servers list in reverse MRU order so that entries are inserted |
| // into |spdy_servers_map|, |alternative_service_map|, and |
| // |server_network_stats_map| from oldest to newest. |
| for (auto it = servers_list->end(); it != servers_list->begin();) { |
| --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_map.get(), |
| alternative_service_map.get(), |
| server_network_stats_map.get(), version)) { |
| detected_corrupted_prefs = true; |
| } |
| } |
| } |
| |
| if (!AddToQuicServerInfoMap(*http_server_properties_dict, |
| quic_server_info_map.get())) { |
| detected_corrupted_prefs = true; |
| } |
| |
| // Read list containing broken and recently-broken alternative services, if |
| // it exists. |
| std::unique_ptr<BrokenAlternativeServiceList> broken_alternative_service_list; |
| std::unique_ptr<RecentlyBrokenAlternativeServices> |
| recently_broken_alternative_services; |
| const base::ListValue* broken_alt_svc_list; |
| if (http_server_properties_dict->GetListWithoutPathExpansion( |
| kBrokenAlternativeServicesKey, &broken_alt_svc_list)) { |
| broken_alternative_service_list = |
| std::make_unique<BrokenAlternativeServiceList>(); |
| recently_broken_alternative_services = |
| std::make_unique<RecentlyBrokenAlternativeServices>(); |
| |
| // Iterate list in reverse-MRU order |
| for (auto it = broken_alt_svc_list->end(); |
| it != broken_alt_svc_list->begin();) { |
| --it; |
| const base::DictionaryValue* entry_dict; |
| if (!it->GetAsDictionary(&entry_dict)) { |
| DVLOG(1) << "Malformed broken alterantive service entry."; |
| detected_corrupted_prefs = true; |
| continue; |
| } |
| if (!AddToBrokenAlternativeServices( |
| *entry_dict, broken_alternative_service_list.get(), |
| recently_broken_alternative_services.get())) { |
| detected_corrupted_prefs = true; |
| continue; |
| } |
| } |
| } |
| |
| // Set the properties loaded from prefs on |http_server_properties_impl_|. |
| |
| UMA_HISTOGRAM_COUNTS_1M("Net.CountOfSpdyServers", spdy_servers_map->size()); |
| http_server_properties_impl_->SetSpdyServers(std::move(spdy_servers_map)); |
| |
| // Update the cached data and use the new alternative service list from |
| // preferences. |
| UMA_HISTOGRAM_COUNTS_1M("Net.CountOfAlternateProtocolServers", |
| alternative_service_map->size()); |
| http_server_properties_impl_->SetAlternativeServiceServers( |
| std::move(alternative_service_map)); |
| |
| http_server_properties_impl_->SetSupportsQuic(*addr); |
| |
| http_server_properties_impl_->SetServerNetworkStats( |
| std::move(server_network_stats_map)); |
| |
| UMA_HISTOGRAM_COUNTS_1000("Net.CountOfQuicServerInfos", |
| quic_server_info_map->size()); |
| |
| http_server_properties_impl_->SetQuicServerInfoMap( |
| std::move(quic_server_info_map)); |
| |
| if (recently_broken_alternative_services) { |
| DCHECK(broken_alternative_service_list); |
| |
| UMA_HISTOGRAM_COUNTS_1000("Net.CountOfBrokenAlternativeServices", |
| broken_alternative_service_list->size()); |
| UMA_HISTOGRAM_COUNTS_1000("Net.CountOfRecentlyBrokenAlternativeServices", |
| recently_broken_alternative_services->size()); |
| |
| http_server_properties_impl_->SetBrokenAndRecentlyBrokenAlternativeServices( |
| std::move(broken_alternative_service_list), |
| std::move(recently_broken_alternative_services)); |
| } |
| |
| // Update the prefs with what we have read (delete all corrupted prefs). |
| if (detected_corrupted_prefs) |
| ScheduleUpdatePrefs(DETECTED_CORRUPTED_PREFS); |
| } |
| |
| bool HttpServerPropertiesManager::AddToBrokenAlternativeServices( |
| const base::DictionaryValue& broken_alt_svc_entry_dict, |
| BrokenAlternativeServiceList* broken_alternative_service_list, |
| RecentlyBrokenAlternativeServices* recently_broken_alternative_services) { |
| AlternativeService alt_service; |
| if (!ParseAlternativeServiceDict(broken_alt_svc_entry_dict, false, |
| "broken alternative services", |
| &alt_service)) { |
| return false; |
| } |
| |
| // Each entry must contain either broken-count and/or broken-until fields. |
| bool contains_broken_count_or_broken_until = false; |
| |
| // Read broken-count and add an entry for |alt_service| into |
| // |recently_broken_alternative_services|. |
| if (broken_alt_svc_entry_dict.HasKey(kBrokenCountKey)) { |
| int broken_count; |
| if (!broken_alt_svc_entry_dict.GetIntegerWithoutPathExpansion( |
| kBrokenCountKey, &broken_count)) { |
| DVLOG(1) << "Recently broken alternative service has malformed " |
| << "broken-count."; |
| return false; |
| } |
| if (broken_count < 0) { |
| DVLOG(1) << "Broken alternative service has negative broken-count."; |
| return false; |
| } |
| recently_broken_alternative_services->Put(alt_service, broken_count); |
| contains_broken_count_or_broken_until = true; |
| } |
| |
| // Read broken-until and add an entry for |alt_service| in |
| // |broken_alternative_service_list|. |
| if (broken_alt_svc_entry_dict.HasKey(kBrokenUntilKey)) { |
| std::string expiration_string; |
| int64_t expiration_int64; |
| if (!broken_alt_svc_entry_dict.GetStringWithoutPathExpansion( |
| kBrokenUntilKey, &expiration_string) || |
| !base::StringToInt64(expiration_string, &expiration_int64)) { |
| DVLOG(1) << "Broken alternative service has malformed broken-until " |
| << "string."; |
| return false; |
| } |
| |
| time_t expiration_time_t = static_cast<time_t>(expiration_int64); |
| // Convert expiration from time_t to Time to TimeTicks |
| base::TimeTicks expiration_time_ticks = |
| clock_->NowTicks() + |
| (base::Time::FromTimeT(expiration_time_t) - base::Time::Now()); |
| broken_alternative_service_list->push_back( |
| std::make_pair(alt_service, expiration_time_ticks)); |
| contains_broken_count_or_broken_until = true; |
| } |
| |
| if (!contains_broken_count_or_broken_until) { |
| DVLOG(1) << "Broken alternative service has neither broken-count nor " |
| << "broken-until specified."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool HttpServerPropertiesManager::AddServersData( |
| const base::DictionaryValue& servers_dict, |
| SpdyServersMap* spdy_servers_map, |
| AlternativeServiceMap* alternative_service_map, |
| ServerNetworkStatsMap* network_stats_map, |
| int version) { |
| for (base::DictionaryValue::Iterator it(servers_dict); !it.IsAtEnd(); |
| it.Advance()) { |
| // Get server's scheme/host/pair. |
| const std::string& server_str = it.key(); |
| std::string spdy_server_url = server_str; |
| if (version < 5) { |
| // For old version disk data, always use HTTPS as the scheme. |
| spdy_server_url.insert(0, "https://"); |
| } |
| url::SchemeHostPort spdy_server((GURL(spdy_server_url))); |
| if (spdy_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_map->Put(spdy_server.Serialize(), supports_spdy); |
| } |
| |
| if (!AddToAlternativeServiceMap(spdy_server, *server_pref_dict, |
| alternative_service_map) || |
| !AddToNetworkStatsMap(spdy_server, *server_pref_dict, |
| network_stats_map)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool HttpServerPropertiesManager::ParseAlternativeServiceDict( |
| const base::DictionaryValue& dict, |
| bool host_optional, |
| const std::string& parsing_under, |
| AlternativeService* alternative_service) { |
| // Protocol is mandatory. |
| std::string protocol_str; |
| if (!dict.GetStringWithoutPathExpansion(kProtocolKey, &protocol_str)) { |
| DVLOG(1) << "Malformed alternative service protocol string under: " |
| << parsing_under; |
| return false; |
| } |
| NextProto protocol = NextProtoFromString(protocol_str); |
| if (!IsAlternateProtocolValid(protocol)) { |
| DVLOG(1) << "Invalid alternative service protocol string \"" << protocol_str |
| << "\" under: " << parsing_under; |
| return false; |
| } |
| alternative_service->protocol = protocol; |
| |
| // If host is optional, it defaults to "". |
| std::string host = ""; |
| if (dict.HasKey(kHostKey)) { |
| if (!dict.GetStringWithoutPathExpansion(kHostKey, &host)) { |
| DVLOG(1) << "Malformed alternative service host string under: " |
| << parsing_under; |
| return false; |
| } |
| } else if (!host_optional) { |
| DVLOG(1) << "alternative service missing host string under: " |
| << parsing_under; |
| return false; |
| } |
| alternative_service->host = host; |
| |
| // Port is mandatory. |
| int port = 0; |
| if (!dict.GetInteger(kPortKey, &port) || !IsPortValid(port)) { |
| DVLOG(1) << "Malformed alternative service port under: " << parsing_under; |
| return false; |
| } |
| alternative_service->port = static_cast<uint32_t>(port); |
| |
| return true; |
| } |
| |
| bool HttpServerPropertiesManager::ParseAlternativeServiceInfoDictOfServer( |
| const base::DictionaryValue& dict, |
| const std::string& server_str, |
| AlternativeServiceInfo* alternative_service_info) { |
| AlternativeService alternative_service; |
| if (!ParseAlternativeServiceDict(dict, true, "server " + server_str, |
| &alternative_service)) { |
| return false; |
| } |
| alternative_service_info->set_alternative_service(alternative_service); |
| |
| // Expiration is optional, defaults to one day. |
| if (!dict.HasKey(kExpirationKey)) { |
| alternative_service_info->set_expiration(base::Time::Now() + |
| base::TimeDelta::FromDays(1)); |
| } else { |
| std::string expiration_string; |
| if (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->set_expiration( |
| base::Time::FromInternalValue(expiration_int64)); |
| } else { |
| DVLOG(1) << "Malformed alternative service expiration for server: " |
| << server_str; |
| return false; |
| } |
| } |
| |
| // Advertised versions list is optional. |
| if (dict.HasKey(kAdvertisedVersionsKey)) { |
| const base::ListValue* versions_list = nullptr; |
| if (!dict.GetListWithoutPathExpansion(kAdvertisedVersionsKey, |
| &versions_list)) { |
| DVLOG(1) << "Malformed alternative service advertised versions list for " |
| << "server: " << server_str; |
| return false; |
| } |
| quic::QuicTransportVersionVector advertised_versions; |
| for (const auto& value : *versions_list) { |
| int version; |
| if (!value.GetAsInteger(&version)) { |
| DVLOG(1) << "Malformed alternative service version for server: " |
| << server_str; |
| return false; |
| } |
| advertised_versions.push_back(quic::QuicTransportVersion(version)); |
| } |
| alternative_service_info->set_advertised_versions(advertised_versions); |
| } |
| |
| return true; |
| } |
| |
| bool HttpServerPropertiesManager::AddToAlternativeServiceMap( |
| const url::SchemeHostPort& 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; |
| } |
| if (server.scheme() != "https") { |
| return false; |
| } |
| |
| AlternativeServiceInfoVector alternative_service_info_vector; |
| for (const auto& 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 (!ParseAlternativeServiceInfoDictOfServer(*alternative_service_dict, |
| server.Serialize(), |
| &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 url::SchemeHostPort& 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.Serialize(); |
| return false; |
| } |
| ServerNetworkStats server_network_stats; |
| server_network_stats.srtt = base::TimeDelta::FromMicroseconds(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(); |
| |
| quic::QuicServerId quic_server_id = |
| QuicServerIdFromString(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; |
| } |
| |
| // |
| // Update Preferences with data from the cached data. |
| // |
| void HttpServerPropertiesManager::ScheduleUpdatePrefs(Location location) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| // Do not schedule a new update if there is already one scheduled. |
| if (network_prefs_update_timer_.IsRunning()) |
| return; |
| |
| network_prefs_update_timer_.Start( |
| FROM_HERE, kUpdatePrefsDelay, |
| base::Bind(&HttpServerPropertiesManager::UpdatePrefsFromCache, |
| base::Unretained(this), base::Passed(base::OnceClosure()))); |
| |
| // TODO(rtenneti): Delete the following histogram after collecting some data. |
| UMA_HISTOGRAM_ENUMERATION("Net.HttpServerProperties.UpdatePrefs", location, |
| HttpServerPropertiesManager::NUM_LOCATIONS); |
| } |
| |
| void HttpServerPropertiesManager::UpdatePrefsFromCache( |
| base::OnceClosure callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| typedef base::MRUCache<url::SchemeHostPort, ServerPref> ServerPrefMap; |
| ServerPrefMap server_pref_map(ServerPrefMap::NO_AUTO_EVICT); |
| |
| // Add SPDY servers to |server_pref_map|. |
| const SpdyServersMap& spdy_servers_map = |
| http_server_properties_impl_->spdy_servers_map(); |
| for (auto it = spdy_servers_map.rbegin(); it != spdy_servers_map.rend(); |
| ++it) { |
| // Only add servers that support SPDY. |
| if (!it->second) |
| continue; |
| |
| url::SchemeHostPort server(GURL(it->first)); |
| auto map_it = server_pref_map.Put(server, ServerPref()); |
| map_it->second.supports_spdy = true; |
| } |
| |
| // Add alternative service info to |server_pref_map|. |
| const AlternativeServiceMap& alternative_service_map = |
| http_server_properties_impl_->alternative_service_map(); |
| UMA_HISTOGRAM_COUNTS_1M("Net.CountOfAlternateProtocolServers.Memory", |
| alternative_service_map.size()); |
| typedef std::map<std::string, bool> CanonicalHostPersistedMap; |
| CanonicalHostPersistedMap persisted_map; |
| const base::Time now = base::Time::Now(); |
| for (auto it = alternative_service_map.rbegin(); |
| it != alternative_service_map.rend(); ++it) { |
| const url::SchemeHostPort& 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() < now) { |
| continue; |
| } |
| if (!IsAlternateProtocolValid( |
| alternative_service_info.alternative_service().protocol)) { |
| continue; |
| } |
| notbroken_alternative_service_info_vector.push_back( |
| alternative_service_info); |
| } |
| if (notbroken_alternative_service_info_vector.empty()) { |
| continue; |
| } |
| const std::string* canonical_suffix = |
| http_server_properties_impl_->GetCanonicalSuffix(server.host()); |
| if (canonical_suffix != nullptr) { |
| if (persisted_map.find(*canonical_suffix) != persisted_map.end()) |
| continue; |
| persisted_map[*canonical_suffix] = true; |
| } |
| |
| auto map_it = server_pref_map.Get(server); |
| if (map_it == server_pref_map.end()) |
| map_it = server_pref_map.Put(server, ServerPref()); |
| map_it->second.alternative_service_info_vector = |
| std::move(notbroken_alternative_service_info_vector); |
| } |
| |
| // Add server network stats to |server_pref_map|. |
| const ServerNetworkStatsMap& server_network_stats_map = |
| http_server_properties_impl_->server_network_stats_map(); |
| for (auto it = server_network_stats_map.rbegin(); |
| it != server_network_stats_map.rend(); ++it) { |
| const url::SchemeHostPort& server = it->first; |
| auto map_it = server_pref_map.Get(server); |
| if (map_it == server_pref_map.end()) |
| map_it = server_pref_map.Put(server, ServerPref()); |
| map_it->second.server_network_stats_valid = true; |
| map_it->second.server_network_stats = it->second; |
| } |
| |
| base::DictionaryValue http_server_properties_dict; |
| |
| // Convert |server_pref_map| to a DictionaryValue and add it to |
| // |http_server_properties_dict|. |
| auto servers_list = std::make_unique<base::ListValue>(); |
| for (ServerPrefMap::const_reverse_iterator map_it = server_pref_map.rbegin(); |
| map_it != server_pref_map.rend(); ++map_it) { |
| const url::SchemeHostPort server = map_it->first; |
| const ServerPref& server_pref = map_it->second; |
| |
| auto servers_dict = std::make_unique<base::DictionaryValue>(); |
| auto server_pref_dict = std::make_unique<base::DictionaryValue>(); |
| |
| if (server_pref.supports_spdy) { |
| server_pref_dict->SetBoolean(kSupportsSpdyKey, server_pref.supports_spdy); |
| } |
| if (!server_pref.alternative_service_info_vector.empty()) { |
| SaveAlternativeServiceToServerPrefs( |
| server_pref.alternative_service_info_vector, server_pref_dict.get()); |
| } |
| if (server_pref.server_network_stats_valid) { |
| SaveNetworkStatsToServerPrefs(server_pref.server_network_stats, |
| server_pref_dict.get()); |
| } |
| |
| servers_dict->SetWithoutPathExpansion(server.Serialize(), |
| std::move(server_pref_dict)); |
| bool value = servers_list->AppendIfNotPresent(std::move(servers_dict)); |
| DCHECK(value); // Should never happen. |
| } |
| http_server_properties_dict.SetWithoutPathExpansion(kServersKey, |
| std::move(servers_list)); |
| |
| SetVersion(&http_server_properties_dict, kVersionNumber); |
| |
| IPAddress last_quic_addr; |
| if (http_server_properties_impl_->GetSupportsQuic(&last_quic_addr)) { |
| SaveSupportsQuicToPrefs(last_quic_addr, &http_server_properties_dict); |
| } |
| |
| SaveQuicServerInfoMapToServerPrefs( |
| http_server_properties_impl_->quic_server_info_map(), |
| &http_server_properties_dict); |
| |
| SaveBrokenAlternativeServicesToPrefs( |
| http_server_properties_impl_->broken_alternative_service_list(), |
| kMaxBrokenAlternativeServicesToPersist, |
| http_server_properties_impl_->recently_broken_alternative_services(), |
| &http_server_properties_dict); |
| |
| setting_prefs_ = true; |
| pref_delegate_->SetServerProperties(http_server_properties_dict, |
| std::move(callback)); |
| setting_prefs_ = false; |
| |
| net_log_.AddEvent(NetLogEventType::HTTP_SERVER_PROPERTIES_UPDATE_PREFS, |
| base::Bind(&NetLogCallback, &http_server_properties_dict)); |
| } |
| |
| void HttpServerPropertiesManager::SaveAlternativeServiceToServerPrefs( |
| const AlternativeServiceInfoVector& alternative_service_info_vector, |
| base::DictionaryValue* server_pref_dict) { |
| if (alternative_service_info_vector.empty()) { |
| return; |
| } |
| std::unique_ptr<base::ListValue> 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)); |
| std::unique_ptr<base::DictionaryValue> alternative_service_dict( |
| new base::DictionaryValue); |
| AddAlternativeServiceFieldsToDictionaryValue( |
| alternative_service, alternative_service_dict.get()); |
| // JSON cannot store int64_t, so expiration is converted to a string. |
| alternative_service_dict->SetString( |
| kExpirationKey, |
| base::Int64ToString( |
| alternative_service_info.expiration().ToInternalValue())); |
| std::unique_ptr<base::ListValue> advertised_versions_list = |
| std::make_unique<base::ListValue>(); |
| for (const auto& version : alternative_service_info.advertised_versions()) { |
| advertised_versions_list->AppendInteger(version); |
| } |
| alternative_service_dict->SetList(kAdvertisedVersionsKey, |
| std::move(advertised_versions_list)); |
| alternative_service_list->Append(std::move(alternative_service_dict)); |
| } |
| if (alternative_service_list->GetSize() == 0) |
| return; |
| server_pref_dict->SetWithoutPathExpansion( |
| kAlternativeServiceKey, std::move(alternative_service_list)); |
| } |
| |
| void HttpServerPropertiesManager::SaveSupportsQuicToPrefs( |
| const IPAddress& last_quic_address, |
| base::DictionaryValue* http_server_properties_dict) { |
| if (!last_quic_address.IsValid()) |
| return; |
| |
| auto supports_quic_dict = std::make_unique<base::DictionaryValue>(); |
| supports_quic_dict->SetBoolean(kUsedQuicKey, true); |
| supports_quic_dict->SetString(kAddressKey, last_quic_address.ToString()); |
| http_server_properties_dict->SetWithoutPathExpansion( |
| kSupportsQuicKey, std::move(supports_quic_dict)); |
| } |
| |
| void HttpServerPropertiesManager::SaveNetworkStatsToServerPrefs( |
| const ServerNetworkStats& server_network_stats, |
| base::DictionaryValue* server_pref_dict) { |
| auto server_network_stats_dict = std::make_unique<base::DictionaryValue>(); |
| // Becasue JSON doesn't support int64_t, persist int64_t as a string. |
| server_network_stats_dict->SetInteger( |
| kSrttKey, static_cast<int>(server_network_stats.srtt.InMicroseconds())); |
| // TODO(rtenneti): When QUIC starts using bandwidth_estimate, then persist |
| // bandwidth_estimate. |
| server_pref_dict->SetWithoutPathExpansion( |
| kNetworkStatsKey, std::move(server_network_stats_dict)); |
| } |
| |
| void HttpServerPropertiesManager::SaveQuicServerInfoMapToServerPrefs( |
| const QuicServerInfoMap& quic_server_info_map, |
| base::DictionaryValue* http_server_properties_dict) { |
| if (quic_server_info_map.empty()) |
| return; |
| auto quic_servers_dict = std::make_unique<base::DictionaryValue>(); |
| for (auto it = quic_server_info_map.rbegin(); |
| it != quic_server_info_map.rend(); ++it) { |
| const quic::QuicServerId& server_id = it->first; |
| auto quic_server_pref_dict = std::make_unique<base::DictionaryValue>(); |
| quic_server_pref_dict->SetKey(kServerInfoKey, base::Value(it->second)); |
| quic_servers_dict->SetWithoutPathExpansion( |
| QuicServerIdToString(server_id), std::move(quic_server_pref_dict)); |
| } |
| http_server_properties_dict->SetWithoutPathExpansion( |
| kQuicServers, std::move(quic_servers_dict)); |
| } |
| |
| void HttpServerPropertiesManager::SaveBrokenAlternativeServicesToPrefs( |
| const BrokenAlternativeServiceList& broken_alternative_service_list, |
| size_t max_broken_alternative_services, |
| const RecentlyBrokenAlternativeServices& |
| recently_broken_alternative_services, |
| base::DictionaryValue* http_server_properties_dict) { |
| if (broken_alternative_service_list.empty() && |
| recently_broken_alternative_services.empty()) |
| return; |
| |
| // JSON list will be in MRU order according to |
| // |recently_broken_alternative_services|. |
| std::unique_ptr<base::ListValue> json_list = |
| std::make_unique<base::ListValue>(); |
| |
| // Maps recently-broken alternative services to the index where it's stored |
| // in |json_list|. |
| std::unordered_map<AlternativeService, size_t, AlternativeServiceHash> |
| json_list_index_map; |
| |
| if (!recently_broken_alternative_services.empty()) { |
| for (auto it = recently_broken_alternative_services.rbegin(); |
| it != recently_broken_alternative_services.rend(); ++it) { |
| const AlternativeService& alt_service = it->first; |
| int broken_count = it->second; |
| base::DictionaryValue entry_dict; |
| AddAlternativeServiceFieldsToDictionaryValue(alt_service, &entry_dict); |
| entry_dict.SetKey(kBrokenCountKey, base::Value(broken_count)); |
| json_list_index_map[alt_service] = json_list->GetList().size(); |
| json_list->GetList().push_back(std::move(entry_dict)); |
| } |
| } |
| |
| if (!broken_alternative_service_list.empty()) { |
| // Add expiration time info from |broken_alternative_service_list| to |
| // the JSON list. |
| size_t count = 0; |
| for (auto it = broken_alternative_service_list.begin(); |
| it != broken_alternative_service_list.end() && |
| count < max_broken_alternative_services; |
| ++it, ++count) { |
| const AlternativeService& alt_service = it->first; |
| base::TimeTicks expiration_time_ticks = it->second; |
| // Convert expiration from TimeTicks to Time to time_t |
| time_t expiration_time_t = |
| (base::Time::Now() + (expiration_time_ticks - clock_->NowTicks())) |
| .ToTimeT(); |
| int64_t expiration_int64 = static_cast<int64_t>(expiration_time_t); |
| |
| auto index_map_it = json_list_index_map.find(alt_service); |
| if (index_map_it != json_list_index_map.end()) { |
| size_t json_list_index = index_map_it->second; |
| base::DictionaryValue* entry_dict = nullptr; |
| bool result = json_list->GetDictionary(json_list_index, &entry_dict); |
| DCHECK(result); |
| DCHECK(!entry_dict->HasKey(kBrokenUntilKey)); |
| entry_dict->SetKey(kBrokenUntilKey, |
| base::Value(base::Int64ToString(expiration_int64))); |
| } else { |
| base::DictionaryValue entry_dict; |
| AddAlternativeServiceFieldsToDictionaryValue(alt_service, &entry_dict); |
| entry_dict.SetKey(kBrokenUntilKey, |
| base::Value(base::Int64ToString(expiration_int64))); |
| json_list->GetList().push_back(std::move(entry_dict)); |
| } |
| } |
| } |
| |
| DCHECK(!json_list->empty()); |
| http_server_properties_dict->SetWithoutPathExpansion( |
| kBrokenAlternativeServicesKey, std::move(json_list)); |
| } |
| |
| void HttpServerPropertiesManager::OnHttpServerPropertiesChanged() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (!setting_prefs_) |
| ScheduleUpdateCache(); |
| } |
| |
| } // namespace net |