Index: net/http/http_server_properties_manager.cc |
diff --git a/net/http/http_server_properties_manager.cc b/net/http/http_server_properties_manager.cc |
index d348d199a43fc746ec5a1f5f2c745078f03ce6a5..aed5d66d2c95aa84d1718862fe2531f8af5469dc 100644 |
--- a/net/http/http_server_properties_manager.cc |
+++ b/net/http/http_server_properties_manager.cc |
@@ -49,6 +49,15 @@ const int kMaxSupportsSpdyServerHostsToPersist = 300; |
// Persist 200 ServerNetworkStats. |
const int kMaxServerNetworkStatsHostsToPersist = 200; |
+// TODO (wangyix): update values for how many broken alt svcs to persist after |
+// looking at histograms showing how many are persisted in the real world. |
+ |
+const int kMaxBrokenAlternativeServicesToPersist = |
+ std::numeric_limits<int>::max(); |
+ |
+const int kMaxRecentlyBrokenAlternativeServicesToPersist = |
+ std::numeric_limits<int>::max(); |
+ |
const char kVersionKey[] = "version"; |
const char kServersKey[] = "servers"; |
const char kSupportsSpdyKey[] = "supports_spdy"; |
@@ -64,6 +73,20 @@ const char kPortKey[] = "port"; |
const char kExpirationKey[] = "expiration"; |
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::DictionaryValue& http_server_properties_dict, |
@@ -84,9 +107,22 @@ HttpServerPropertiesManager::HttpServerPropertiesManager( |
scoped_refptr<base::SingleThreadTaskRunner> pref_task_runner, |
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, |
NetLog* net_log) |
+ : HttpServerPropertiesManager(pref_delegate, |
+ pref_task_runner, |
+ network_task_runner, |
+ net_log, |
+ nullptr) {} |
+ |
+HttpServerPropertiesManager::HttpServerPropertiesManager( |
+ PrefDelegate* pref_delegate, |
+ scoped_refptr<base::SingleThreadTaskRunner> pref_task_runner, |
+ scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, |
+ NetLog* net_log, |
+ base::TickClock* clock) |
: pref_task_runner_(std::move(pref_task_runner)), |
pref_delegate_(pref_delegate), |
setting_prefs_(false), |
+ clock_(clock ? clock : &default_clock_), |
is_initialized_(false), |
network_task_runner_(std::move(network_task_runner)), |
net_log_( |
@@ -94,6 +130,7 @@ HttpServerPropertiesManager::HttpServerPropertiesManager( |
NetLogSourceType::HTTP_SERVER_PROPERTIES)) { |
DCHECK(pref_task_runner_->RunsTasksInCurrentSequence()); |
DCHECK(pref_delegate_); |
+ DCHECK(clock_); |
pref_weak_ptr_factory_.reset( |
new base::WeakPtrFactory<HttpServerPropertiesManager>(this)); |
pref_weak_ptr_ = pref_weak_ptr_factory_->GetWeakPtr(); |
@@ -115,7 +152,7 @@ void HttpServerPropertiesManager::InitializeOnNetworkSequence() { |
network_weak_ptr_factory_.reset( |
new base::WeakPtrFactory<HttpServerPropertiesManager>(this)); |
- http_server_properties_impl_.reset(new HttpServerPropertiesImpl()); |
+ http_server_properties_impl_.reset(new HttpServerPropertiesImpl(clock_)); |
network_prefs_update_timer_.reset(new base::OneShotTimer); |
network_prefs_update_timer_->SetTaskRunner(network_task_runner_); |
@@ -533,6 +570,38 @@ void HttpServerPropertiesManager::UpdateCacheFromPrefsOnPrefSequence() { |
detected_corrupted_prefs = true; |
} |
+ // Read broken alternnative services list 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 = |
+ base::MakeUnique<BrokenAlternativeServiceList>(); |
+ recently_broken_alternative_services = |
+ base::MakeUnique<RecentlyBrokenAlternativeServices>( |
+ RecentlyBrokenAlternativeServices::NO_AUTO_EVICT); |
+ |
+ // 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; |
+ } |
+ } |
+ } |
+ |
network_task_runner_->PostTask( |
FROM_HERE, |
base::Bind( |
@@ -540,7 +609,61 @@ void HttpServerPropertiesManager::UpdateCacheFromPrefsOnPrefSequence() { |
base::Unretained(this), base::Passed(&spdy_servers_map), |
base::Passed(&alternative_service_map), base::Passed(&addr), |
base::Passed(&server_network_stats_map), |
- base::Passed(&quic_server_info_map), detected_corrupted_prefs)); |
+ base::Passed(&quic_server_info_map), |
+ base::Passed(&broken_alternative_service_list), |
+ base::Passed(&recently_broken_alternative_services), |
+ 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, true, |
+ "broken alternative services", |
+ &alt_service)) { |
+ return false; |
+ } |
+ |
+ // Read broken-count and add an entry for |alt_service| into |
+ // |recently_broken_alternative_services|. |
+ int broken_count; |
+ if (!broken_alt_svc_entry_dict.GetIntegerWithoutPathExpansion( |
+ kBrokenCountKey, &broken_count)) { |
+ DVLOG(1) << "Recently broken alternative service is missing " |
+ << "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); |
+ |
+ // Read broken-until (optional) and if it exists, 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)) { |
+ 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)); |
+ } else { |
+ DVLOG(1) << "Broken alternative service has malformed broken-until " |
+ << "string."; |
+ return false; |
+ } |
+ } |
+ |
+ return true; |
} |
bool HttpServerPropertiesManager::AddServersData( |
@@ -588,56 +711,65 @@ bool HttpServerPropertiesManager::AddServersData( |
} |
bool HttpServerPropertiesManager::ParseAlternativeServiceDict( |
- const base::DictionaryValue& alternative_service_dict, |
- const std::string& server_str, |
- AlternativeServiceInfo* alternative_service_info) { |
+ const base::DictionaryValue& dict, |
+ bool host_required, |
+ const std::string& parsing_under, |
+ AlternativeService* alternative_service) { |
// Protocol is mandatory. |
std::string protocol_str; |
- if (!alternative_service_dict.GetStringWithoutPathExpansion(kProtocolKey, |
- &protocol_str)) { |
- DVLOG(1) << "Malformed alternative service protocol string for server: " |
- << server_str; |
+ 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 for server: " |
- << server_str; |
+ DVLOG(1) << "Invalid alternative service protocol string \"" << protocol_str |
+ << "\" under: " << parsing_under; |
return false; |
} |
- alternative_service_info->set_protocol(protocol); |
+ alternative_service->protocol = protocol; |
- // Host is optional, defaults to "". |
+ // If host is optional, it defaults to "". |
std::string host = ""; |
- if (alternative_service_dict.HasKey(kHostKey) && |
- !alternative_service_dict.GetStringWithoutPathExpansion(kHostKey, |
- &host)) { |
- DVLOG(1) << "Malformed alternative service host string for server: " |
- << server_str; |
+ if (dict.HasKey(kHostKey)) { |
+ if (!dict.GetStringWithoutPathExpansion(kHostKey, &host)) { |
+ DVLOG(1) << "Malformed alternative service host string under: " |
+ << parsing_under; |
+ return false; |
+ } |
+ } else if (host_required) { |
+ DVLOG(1) << "alternative service missing host string under: " |
+ << parsing_under; |
return false; |
} |
- alternative_service_info->set_host(host); |
+ alternative_service->host = host; |
// Port is mandatory. |
int port = 0; |
- if (!alternative_service_dict.GetInteger(kPortKey, &port) || |
- !IsPortValid(port)) { |
- DVLOG(1) << "Malformed alternative service port for server: " << server_str; |
+ if (!dict.GetInteger(kPortKey, &port) || !IsPortValid(port)) { |
+ DVLOG(1) << "Malformed alternative service port under: " << parsing_under; |
return false; |
} |
- alternative_service_info->set_port(static_cast<uint32_t>(port)); |
+ alternative_service->port = static_cast<uint32_t>(port); |
+ |
+ return true; |
+} |
+bool HttpServerPropertiesManager::ParseAlternativeServiceInfoExpiration( |
+ const base::DictionaryValue& dict, |
+ const std::string& server_str, |
+ AlternativeServiceInfo* alternative_service_info) { |
// Expiration is optional, defaults to one day. |
base::Time expiration; |
- if (!alternative_service_dict.HasKey(kExpirationKey)) { |
+ if (!dict.HasKey(kExpirationKey)) { |
alternative_service_info->set_expiration(base::Time::Now() + |
base::TimeDelta::FromDays(1)); |
return true; |
} |
std::string expiration_string; |
- if (alternative_service_dict.GetStringWithoutPathExpansion( |
- kExpirationKey, &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: " |
@@ -676,11 +808,17 @@ bool HttpServerPropertiesManager::AddToAlternativeServiceMap( |
&alternative_service_dict)) |
return false; |
AlternativeServiceInfo alternative_service_info; |
- if (!ParseAlternativeServiceDict(*alternative_service_dict, |
+ AlternativeService alternative_service; |
+ if (!ParseAlternativeServiceDict(*alternative_service_dict, false, |
server.Serialize(), |
- &alternative_service_info)) { |
+ &alternative_service) || |
+ !ParseAlternativeServiceInfoExpiration(*alternative_service_dict, |
+ server.Serialize(), |
+ &alternative_service_info)) { |
return false; |
} |
+ alternative_service_info.set_alternative_service(alternative_service); |
+ |
if (base::Time::Now() < alternative_service_info.expiration()) { |
alternative_service_info_vector.push_back(alternative_service_info); |
} |
@@ -798,6 +936,10 @@ void HttpServerPropertiesManager::UpdateCacheFromPrefsOnNetworkSequence( |
std::unique_ptr<IPAddress> last_quic_address, |
std::unique_ptr<ServerNetworkStatsMap> server_network_stats_map, |
std::unique_ptr<QuicServerInfoMap> quic_server_info_map, |
+ std::unique_ptr<BrokenAlternativeServiceList> |
+ broken_alternative_service_list, |
+ std::unique_ptr<RecentlyBrokenAlternativeServices> |
+ recently_broken_alternative_services, |
bool detected_corrupted_prefs) { |
// Preferences have the master data because admins might have pushed new |
// preferences. Update the cached data with new data from preferences. |
@@ -824,6 +966,19 @@ void HttpServerPropertiesManager::UpdateCacheFromPrefsOnNetworkSequence( |
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) |
ScheduleUpdatePrefsOnNetworkSequence(DETECTED_CORRUPTED_PREFS); |
@@ -891,9 +1046,6 @@ void HttpServerPropertiesManager::UpdatePrefsFromCacheOnNetworkSequence( |
if (alternative_service.host.empty()) { |
alternative_service.host = server.host(); |
} |
- if (IsAlternativeServiceBroken(alternative_service)) { |
- continue; |
- } |
notbroken_alternative_service_info_vector.push_back( |
alternative_service_info); |
} |
@@ -939,6 +1091,43 @@ void HttpServerPropertiesManager::UpdatePrefsFromCacheOnNetworkSequence( |
} |
} |
+ // Make copy of list of broken alternative services stored in |
+ // |http_server_properties_impl_| |
+ std::unique_ptr<BrokenAlternativeServiceList> broken_alt_svc_list_copy; |
+ const BrokenAlternativeServiceList& broken_alternative_service_list = |
+ http_server_properties_impl_->broken_alternative_service_list(); |
+ if (broken_alternative_service_list.size() > 0) { |
+ broken_alt_svc_list_copy = base::MakeUnique<BrokenAlternativeServiceList>(); |
+ size_t count = 0; |
+ for (auto it = broken_alternative_service_list.begin(); |
+ it != broken_alternative_service_list.end() && |
+ count < kMaxBrokenAlternativeServicesToPersist; |
+ ++it) { |
+ broken_alt_svc_list_copy->push_back(*it); |
+ ++count; |
+ } |
+ } |
+ |
+ // Make copy of RecentlyBrokenAternativeServices stored in |
+ // |http_server_properties_impl_|. |
+ std::unique_ptr<RecentlyBrokenAlternativeServices> |
+ recently_broken_alt_svc_copy; |
+ const RecentlyBrokenAlternativeServices& recently_broken_alt_services = |
+ http_server_properties_impl_->recently_broken_alternative_services(); |
+ if (recently_broken_alt_services.size() > 0) { |
+ recently_broken_alt_svc_copy = |
+ base::MakeUnique<RecentlyBrokenAlternativeServices>( |
+ kMaxRecentlyBrokenAlternativeServicesToPersist); |
+ size_t count = 0; |
+ for (auto it = recently_broken_alt_services.rbegin(); |
+ it != recently_broken_alt_services.rend() && |
+ count < kMaxRecentlyBrokenAlternativeServicesToPersist; |
+ ++it) { |
+ recently_broken_alt_svc_copy->Put(it->first, it->second); |
+ ++count; |
+ } |
+ } |
+ |
std::unique_ptr<IPAddress> last_quic_addr = base::MakeUnique<IPAddress>(); |
http_server_properties_impl_->GetSupportsQuic(last_quic_addr.get()); |
// Update the preferences on the pref thread. |
@@ -949,7 +1138,9 @@ void HttpServerPropertiesManager::UpdatePrefsFromCacheOnNetworkSequence( |
base::Passed(&alternative_service_map), |
base::Passed(&last_quic_addr), |
base::Passed(&server_network_stats_map), |
- base::Passed(&quic_server_info_map), completion)); |
+ base::Passed(&quic_server_info_map), |
+ base::Passed(&broken_alt_svc_list_copy), |
+ base::Passed(&recently_broken_alt_svc_copy), completion)); |
} |
// A local or temporary data structure to hold |supports_spdy|, SpdySettings, |
@@ -987,6 +1178,10 @@ void HttpServerPropertiesManager::UpdatePrefsOnPrefThread( |
std::unique_ptr<IPAddress> last_quic_address, |
std::unique_ptr<ServerNetworkStatsMap> server_network_stats_map, |
std::unique_ptr<QuicServerInfoMap> quic_server_info_map, |
+ std::unique_ptr<BrokenAlternativeServiceList> |
+ broken_alternative_service_list, |
+ std::unique_ptr<RecentlyBrokenAlternativeServices> |
+ recently_broken_alternative_services, |
const base::Closure& completion) { |
typedef base::MRUCache<url::SchemeHostPort, ServerPref> ServerPrefMap; |
ServerPrefMap server_pref_map(ServerPrefMap::NO_AUTO_EVICT); |
@@ -1081,6 +1276,10 @@ void HttpServerPropertiesManager::UpdatePrefsOnPrefThread( |
&http_server_properties_dict); |
} |
+ SaveBrokenAlternativeServicesToPrefs( |
+ broken_alternative_service_list.get(), |
+ recently_broken_alternative_services.get(), &http_server_properties_dict); |
+ |
setting_prefs_ = true; |
pref_delegate_->SetServerProperties(http_server_properties_dict); |
setting_prefs_ = false; |
@@ -1105,17 +1304,13 @@ void HttpServerPropertiesManager::SaveAlternativeServiceToServerPrefs( |
new base::ListValue); |
for (const AlternativeServiceInfo& alternative_service_info : |
alternative_service_info_vector) { |
- const AlternativeService alternative_service = |
+ 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); |
- alternative_service_dict->SetInteger(kPortKey, alternative_service.port); |
- if (!alternative_service.host.empty()) { |
- alternative_service_dict->SetString(kHostKey, alternative_service.host); |
- } |
- alternative_service_dict->SetString( |
- kProtocolKey, NextProtoToString(alternative_service.protocol)); |
+ AddAlternativeServiceFieldsToDictionaryValue( |
+ alternative_service, alternative_service_dict.get()); |
// JSON cannot store int64_t, so expiration is converted to a string. |
alternative_service_dict->SetString( |
kExpirationKey, |
@@ -1173,6 +1368,59 @@ void HttpServerPropertiesManager::SaveQuicServerInfoMapToServerPrefs( |
kQuicServers, std::move(quic_servers_dict)); |
} |
+void HttpServerPropertiesManager::SaveBrokenAlternativeServicesToPrefs( |
+ const BrokenAlternativeServiceList* broken_alternative_service_list, |
+ const RecentlyBrokenAlternativeServices* |
+ recently_broken_alternative_services, |
+ base::DictionaryValue* http_server_properties_dict) { |
+ if (!recently_broken_alternative_services) { |
+ DCHECK(!broken_alternative_service_list); |
+ return; |
+ } |
+ |
+ // JSON list will be in MRU order according to |
+ // |recently_broken_alternative_services|. |
+ base::ListValue* json_list = new base::ListValue; |
+ // Maps an alternative service to the index where it's stored in |json_list|. |
+ std::unordered_map<AlternativeService, size_t, AlternativeServiceHash> |
+ json_list_index_map; |
+ 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.SetIntegerWithoutPathExpansion(kBrokenCountKey, 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) |
+ return; |
+ |
+ // Add expiration time info from |broken_alternative_service_list| to |
+ // the JSON list. |
+ for (auto entry : *broken_alternative_service_list) { |
+ const AlternativeService& alt_service = entry.first; |
+ base::TimeTicks expiration_time_ticks = entry.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); |
+ |
+ size_t json_list_index = json_list_index_map[alt_service]; |
+ base::DictionaryValue* entry_dict; |
+ DCHECK(json_list->GetDictionary(json_list_index, &entry_dict)); |
+ entry_dict->SetStringWithoutPathExpansion( |
+ kBrokenUntilKey, base::Int64ToString(expiration_int64)); |
+ } |
+ |
+ http_server_properties_dict->SetWithoutPathExpansion( |
+ kBrokenAlternativeServicesKey, |
+ base::WrapUnique<base::ListValue>(json_list)); |
+} |
+ |
void HttpServerPropertiesManager::OnHttpServerPropertiesChanged() { |
DCHECK(pref_task_runner_->RunsTasksInCurrentSequence()); |
if (!setting_prefs_) |