mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-12-01 01:36:09 +03:00
1319 lines
50 KiB
C++
1319 lines
50 KiB
C++
// 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 (base::ListValue::const_iterator 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 (base::ListValue::const_iterator 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 (SpdyServersMap::const_reverse_iterator 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 (AlternativeServiceMap::const_reverse_iterator 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 (ServerNetworkStatsMap::const_reverse_iterator 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 (QuicServerInfoMap::const_reverse_iterator 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
|